diff options
493 files changed, 14016 insertions, 6760 deletions
diff --git a/apct-tests/perftests/surfaceflinger/AndroidTest.xml b/apct-tests/perftests/surfaceflinger/AndroidTest.xml index 53e5d99409e2..11d110670b7a 100644 --- a/apct-tests/perftests/surfaceflinger/AndroidTest.xml +++ b/apct-tests/perftests/surfaceflinger/AndroidTest.xml @@ -49,16 +49,20 @@ <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. --> <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> + <option name="instrumentation-arg" key="profiling-iterations" value="525" /> <!-- PerfettoListener related arguments --> <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" /> <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" /> <!-- SimpleperfListener related arguments --> <option name="instrumentation-arg" key="report" value="true" /> - <option name="instrumentation-arg" key="arguments" value="""" /> + <option name="instrumentation-arg" key="arguments" value="-g" /> <option name="instrumentation-arg" key="events_to_record" value="instructions,cpu-cycles,raw-l3d-cache-refill,sched:sched_waking" /> <option name="instrumentation-arg" key="processes_to_record" value="surfaceflinger" /> - <option name="instrumentation-arg" key="symbols_to_report" value=""android::SurfaceFlinger::commit(;android::SurfaceFlinger::composite("" /> + <option name="instrumentation-arg" key="symbols_to_report" value=""commit;android::SurfaceFlinger::commit(;composite;android::SurfaceFlinger::composite("" /> + + <!-- should match profiling-iterations --> + <option name="instrumentation-arg" key="test_iterations" value="525" /> <!-- ProcLoadListener related arguments --> <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run --> diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/BufferFlinger.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/BufferFlinger.java index 52fb8a6023f1..8a447bb801f2 100644 --- a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/BufferFlinger.java +++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/BufferFlinger.java @@ -53,15 +53,17 @@ public class BufferFlinger { } } - public void addBuffer(SurfaceControl.Transaction t, SurfaceControl surfaceControl) - throws InterruptedException { - GraphicBuffer buffer = mBufferQ.take(); - t.setBuffer(surfaceControl, - HardwareBuffer.createFromGraphicBuffer(buffer), - null, - (SyncFence fence) -> { - releaseCallback(fence, buffer); - }); + public void addBuffer(SurfaceControl.Transaction t, SurfaceControl surfaceControl) { + try { + final GraphicBuffer buffer = mBufferQ.take(); + t.setBuffer(surfaceControl, + HardwareBuffer.createFromGraphicBuffer(buffer), + null, + (SyncFence fence) -> { + releaseCallback(fence, buffer); + }); + } catch (InterruptedException ignore) { + } } public void releaseCallback(SyncFence fence, GraphicBuffer buffer) { diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java index 45d164c96cd8..f92c297936dd 100644 --- a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java +++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java @@ -18,8 +18,8 @@ package android.surfaceflinger; import android.graphics.Bitmap; import android.graphics.Color; -import android.perftests.utils.BenchmarkState; -import android.perftests.utils.PerfStatusReporter; +import android.os.Bundle; +import android.util.Log; import android.view.SurfaceControl; import androidx.test.ext.junit.rules.ActivityScenarioRule; @@ -29,6 +29,7 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.RuleChain; @@ -40,37 +41,66 @@ import java.util.Random; @LargeTest @RunWith(AndroidJUnit4.class) public class SurfaceFlingerPerfTest { - protected ActivityScenarioRule<SurfaceFlingerTestActivity> mActivityRule = + private static final String TAG = "SurfaceFlingerPerfTest"; + private final ActivityScenarioRule<SurfaceFlingerTestActivity> mActivityRule = new ActivityScenarioRule<>(SurfaceFlingerTestActivity.class); - protected PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); private SurfaceFlingerTestActivity mActivity; - static final int BUFFER_COUNT = 2; + private static final int BUFFER_COUNT = 2; + private static final int MAX_BUFFERS = 10; + private static final int MAX_POSITION = 10; + private static final float MAX_SCALE = 2.0f; + + private static final String ARGUMENT_PROFILING_ITERATIONS = "profiling-iterations"; + private static final String DEFAULT_PROFILING_ITERATIONS = "100"; + private static int sProfilingIterations; + private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); @Rule public final RuleChain mAllRules = RuleChain - .outerRule(mPerfStatusReporter) - .around(mActivityRule); + .outerRule(mActivityRule); + + @BeforeClass + public static void suiteSetup() { + final Bundle arguments = InstrumentationRegistry.getArguments(); + sProfilingIterations = Integer.parseInt( + arguments.getString(ARGUMENT_PROFILING_ITERATIONS, DEFAULT_PROFILING_ITERATIONS)); + Log.d(TAG, "suiteSetup: mProfilingIterations = " + sProfilingIterations); + } + @Before public void setup() { mActivityRule.getScenario().onActivity(activity -> mActivity = activity); + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + for (int i = 0; i < MAX_BUFFERS; i++) { + SurfaceControl sc = createSurfaceControl(); + BufferFlinger bufferTracker = createBufferTracker(Color.argb(getRandomColorComponent(), + getRandomColorComponent(), getRandomColorComponent(), + getRandomColorComponent())); + bufferTracker.addBuffer(t, sc); + t.setPosition(sc, i * 10, i * 10); + } + t.apply(true); } @After public void teardown() { mSurfaceControls.forEach(SurfaceControl::release); - mByfferTrackers.forEach(BufferFlinger::freeBuffers); + mBufferTrackers.forEach(BufferFlinger::freeBuffers); } + static int getRandomColorComponent() { + return new Random().nextInt(155) + 100; + } - private ArrayList<BufferFlinger> mByfferTrackers = new ArrayList<>(); + private final ArrayList<BufferFlinger> mBufferTrackers = new ArrayList<>(); private BufferFlinger createBufferTracker(int color) { BufferFlinger bufferTracker = new BufferFlinger(BUFFER_COUNT, color); - mByfferTrackers.add(bufferTracker); + mBufferTrackers.add(bufferTracker); return bufferTracker; } - private ArrayList<SurfaceControl> mSurfaceControls = new ArrayList<>(); - private SurfaceControl createSurfaceControl() throws InterruptedException { + private final ArrayList<SurfaceControl> mSurfaceControls = new ArrayList<>(); + private SurfaceControl createSurfaceControl() { SurfaceControl sc = mActivity.createChildSurfaceControl(); mSurfaceControls.add(sc); return sc; @@ -78,151 +108,90 @@ public class SurfaceFlingerPerfTest { @Test public void singleBuffer() throws Exception { - SurfaceControl sc = createSurfaceControl(); - BufferFlinger bufferTracker = createBufferTracker(Color.GREEN); - SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - bufferTracker.addBuffer(t, sc); - t.show(sc); - - BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - while (state.keepRunning()) { - bufferTracker.addBuffer(t, sc); - t.apply(); + for (int i = 0; i < sProfilingIterations; i++) { + mBufferTrackers.get(0).addBuffer(mTransaction, mSurfaceControls.get(0)); + mTransaction.show(mSurfaceControls.get(0)).apply(true); } } - static int getRandomColorComponent() { - return new Random().nextInt(155) + 100; - } - @Test public void multipleBuffers() throws Exception { - final int MAX_BUFFERS = 10; - - SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - for (int i = 0; i < MAX_BUFFERS; i++) { - SurfaceControl sc = createSurfaceControl(); - BufferFlinger bufferTracker = createBufferTracker(Color.argb(getRandomColorComponent(), - getRandomColorComponent(), getRandomColorComponent(), - getRandomColorComponent())); - bufferTracker.addBuffer(t, sc); - t.setPosition(sc, i * 10, i * 10); - t.show(sc); - } - t.apply(true); - BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - while (state.keepRunning()) { + for (int j = 0; j < sProfilingIterations; j++) { for (int i = 0; i < MAX_BUFFERS; i++) { - mByfferTrackers.get(i).addBuffer(t, mSurfaceControls.get(i)); + mBufferTrackers.get(i).addBuffer(mTransaction, mSurfaceControls.get(i)); + mTransaction.show(mSurfaceControls.get(i)); } - t.apply(); + mTransaction.apply(true); } } @Test public void multipleOpaqueBuffers() throws Exception { - final int MAX_BUFFERS = 10; - - SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - for (int i = 0; i < MAX_BUFFERS; i++) { - SurfaceControl sc = createSurfaceControl(); - BufferFlinger bufferTracker = createBufferTracker(Color.rgb(getRandomColorComponent(), - getRandomColorComponent(), getRandomColorComponent())); - bufferTracker.addBuffer(t, sc); - t.setOpaque(sc, true); - t.setPosition(sc, i * 10, i * 10); - t.show(sc); - } - t.apply(true); - BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - while (state.keepRunning()) { + for (int j = 0; j < sProfilingIterations; j++) { for (int i = 0; i < MAX_BUFFERS; i++) { - mByfferTrackers.get(i).addBuffer(t, mSurfaceControls.get(i)); + mBufferTrackers.get(i).addBuffer(mTransaction, mSurfaceControls.get(i)); + mTransaction.show(mSurfaceControls.get(i)).setOpaque(mSurfaceControls.get(i), true); } - t.apply(); + mTransaction.apply(true); } } @Test public void geometryChanges() throws Exception { - final int MAX_POSITION = 10; - final float MAX_SCALE = 2.0f; - - SurfaceControl sc = createSurfaceControl(); - BufferFlinger bufferTracker = createBufferTracker(Color.GREEN); - SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - bufferTracker.addBuffer(t, sc); - t.show(sc).apply(true); - int step = 0; - BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - while (state.keepRunning()) { + for (int i = 0; i < sProfilingIterations; i++) { step = ++step % MAX_POSITION; - t.setPosition(sc, step, step); + mTransaction.setPosition(mSurfaceControls.get(0), step, step); float scale = ((step * MAX_SCALE) / MAX_POSITION) + 0.5f; - t.setScale(sc, scale, scale); - t.apply(); + mTransaction.setScale(mSurfaceControls.get(0), scale, scale); + mTransaction.show(mSurfaceControls.get(0)).apply(true); } } @Test public void geometryWithBufferChanges() throws Exception { - final int MAX_POSITION = 10; - - SurfaceControl sc = createSurfaceControl(); - BufferFlinger bufferTracker = createBufferTracker(Color.GREEN); - SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - bufferTracker.addBuffer(t, sc); - t.show(sc).apply(true); - int step = 0; - BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - while (state.keepRunning()) { + for (int i = 0; i < sProfilingIterations; i++) { step = ++step % MAX_POSITION; - t.setPosition(sc, step, step); - float scale = ((step * 2.0f) / MAX_POSITION) + 0.5f; - t.setScale(sc, scale, scale); - bufferTracker.addBuffer(t, sc); - t.apply(); + mTransaction.setPosition(mSurfaceControls.get(0), step, step); + float scale = ((step * MAX_SCALE) / MAX_POSITION) + 0.5f; + mTransaction.setScale(mSurfaceControls.get(0), scale, scale); + mBufferTrackers.get(0).addBuffer(mTransaction, mSurfaceControls.get(0)); + mTransaction.show(mSurfaceControls.get(0)).apply(true); } } @Test public void addRemoveLayers() throws Exception { - SurfaceControl sc = createSurfaceControl(); - BufferFlinger bufferTracker = createBufferTracker(Color.GREEN); - SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - - BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - while (state.keepRunning()) { + for (int i = 0; i < sProfilingIterations; i++) { SurfaceControl childSurfaceControl = new SurfaceControl.Builder() .setName("childLayer").setBLASTLayer().build(); - bufferTracker.addBuffer(t, childSurfaceControl); - t.reparent(childSurfaceControl, sc); - t.apply(); - t.remove(childSurfaceControl).apply(); + mBufferTrackers.get(0).addBuffer(mTransaction, childSurfaceControl); + mTransaction.reparent(childSurfaceControl, mSurfaceControls.get(0)); + mTransaction.show(childSurfaceControl).show(mSurfaceControls.get(0)); + mTransaction.apply(true); + mTransaction.remove(childSurfaceControl).apply(true); } } @Test public void displayScreenshot() throws Exception { - BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - while (state.keepRunning()) { + for (int i = 0; i < sProfilingIterations; i++) { Bitmap screenshot = InstrumentationRegistry.getInstrumentation().getUiAutomation().takeScreenshot(); screenshot.recycle(); + mTransaction.apply(true); } } @Test public void layerScreenshot() throws Exception { - BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - while (state.keepRunning()) { + for (int i = 0; i < sProfilingIterations; i++) { Bitmap screenshot = InstrumentationRegistry.getInstrumentation().getUiAutomation().takeScreenshot( mActivity.getWindow()); screenshot.recycle(); + mTransaction.apply(true); } } - } diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java index 832a0cd1e917..bb956596c7eb 100644 --- a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java +++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java @@ -22,6 +22,7 @@ import android.os.Bundle; import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.SurfaceView; +import android.view.Window; import android.view.WindowManager; import java.util.concurrent.CountDownLatch; @@ -38,12 +39,15 @@ public class SurfaceFlingerTestActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); mTestSurfaceView = new TestSurfaceView(this); setContentView(mTestSurfaceView); } - public SurfaceControl createChildSurfaceControl() throws InterruptedException { + public SurfaceControl createChildSurfaceControl() { return mTestSurfaceView.getChildSurfaceControlHelper(); } @@ -65,8 +69,11 @@ public class SurfaceFlingerTestActivity extends Activity { }); } - public SurfaceControl getChildSurfaceControlHelper() throws InterruptedException { - mIsReady.await(); + public SurfaceControl getChildSurfaceControlHelper() { + try { + mIsReady.await(); + } catch (InterruptedException ignore) { + } SurfaceHolder holder = getHolder(); // check to see if surface is valid diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java index dfa1442a3192..fcfb45c5f1df 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java @@ -742,6 +742,10 @@ public final class JobStore { } } catch (XmlPullParserException | IOException e) { Slog.wtf(TAG, "Error jobstore xml.", e); + } catch (Exception e) { + // Crashing at this point would result in a boot loop, so live with a general + // Exception for system stability's sake. + Slog.wtf(TAG, "Unexpected exception", e); } finally { if (mPersistInfo.countAllJobsLoaded < 0) { // Only set them once. mPersistInfo.countAllJobsLoaded = numJobs; @@ -890,6 +894,9 @@ public final class JobStore { } catch (IOException e) { Slog.d(TAG, "Error I/O Exception.", e); return null; + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Constraints contained invalid data", e); + return null; } parser.next(); // Consume </constraints> @@ -986,8 +993,14 @@ public final class JobStore { return null; } - PersistableBundle extras = PersistableBundle.restoreFromXml(parser); - jobBuilder.setExtras(extras); + final PersistableBundle extras; + try { + extras = PersistableBundle.restoreFromXml(parser); + jobBuilder.setExtras(extras); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Persisted extras contained invalid data", e); + return null; + } parser.nextTag(); // Consume </extras> final JobInfo builtJob; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java index 5ef6855151cc..30eacf3c7c70 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java @@ -68,6 +68,11 @@ public final class BatteryController extends RestrictingController { */ private final ArraySet<JobStatus> mChangedJobs = new ArraySet<>(); + @GuardedBy("mLock") + private Boolean mLastReportedStatsdBatteryNotLow = null; + @GuardedBy("mLock") + private Boolean mLastReportedStatsdStablePower = null; + public BatteryController(JobSchedulerService service) { super(service); mPowerTracker = new PowerTracker(); @@ -173,6 +178,19 @@ public final class BatteryController extends RestrictingController { Slog.d(TAG, "maybeReportNewChargingStateLocked: " + powerConnected + "/" + stablePower + "/" + batteryNotLow); } + + if (mLastReportedStatsdStablePower == null + || mLastReportedStatsdStablePower != stablePower) { + logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_CHARGING, stablePower); + mLastReportedStatsdStablePower = stablePower; + } + if (mLastReportedStatsdBatteryNotLow == null + || mLastReportedStatsdBatteryNotLow != stablePower) { + logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_BATTERY_NOT_LOW, + batteryNotLow); + mLastReportedStatsdBatteryNotLow = batteryNotLow; + } + final long nowElapsed = sElapsedRealtimeClock.millis(); for (int i = mTrackedTasks.size() - 1; i >= 0; i--) { final JobStatus ts = mTrackedTasks.valueAt(i); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java index f6de109d7ec9..abbe177c5d49 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java @@ -153,6 +153,8 @@ public final class DeviceIdleJobsController extends StateController { changed = true; } mDeviceIdleMode = enabled; + logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_DEVICE_NOT_DOZING, + !mDeviceIdleMode); if (DEBUG) Slog.d(TAG, "mDeviceIdleMode=" + mDeviceIdleMode); mDeviceIdleUpdateFunctor.prepare(); if (enabled) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java index a6fae2c28898..8311dc38f87d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java @@ -93,6 +93,8 @@ public final class IdleController extends RestrictingController implements Idlen @Override public void reportNewIdleState(boolean isIdle) { synchronized (mLock) { + logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_IDLE, isIdle); + final long nowElapsed = sElapsedRealtimeClock.millis(); for (int i = mTrackedTasks.size()-1; i >= 0; i--) { mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(nowElapsed, isIdle); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index 866dc41d8dfb..0d85dfd3b951 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -151,13 +151,12 @@ public final class JobStatus { */ private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER | CONSTRAINT_DEADLINE - | CONSTRAINT_IDLE | CONSTRAINT_PREFETCH | CONSTRAINT_TARE_WEALTH | CONSTRAINT_TIMING_DELAY | CONSTRAINT_WITHIN_QUOTA; - // TODO(b/129954980) + // TODO(b/129954980): ensure this doesn't spam statsd, especially at boot private static final boolean STATS_LOG_ENABLED = false; // No override. @@ -1864,7 +1863,7 @@ public final class JobStatus { } /** Returns a {@link JobServerProtoEnums.Constraint} enum value for the given constraint. */ - private int getProtoConstraint(int constraint) { + static int getProtoConstraint(int constraint) { switch (constraint) { case CONSTRAINT_BACKGROUND_NOT_RESTRICTED: return JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED; @@ -1882,8 +1881,12 @@ public final class JobStatus { return JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING; case CONSTRAINT_IDLE: return JobServerProtoEnums.CONSTRAINT_IDLE; + case CONSTRAINT_PREFETCH: + return JobServerProtoEnums.CONSTRAINT_PREFETCH; case CONSTRAINT_STORAGE_NOT_LOW: return JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW; + case CONSTRAINT_TARE_WEALTH: + return JobServerProtoEnums.CONSTRAINT_TARE_WEALTH; case CONSTRAINT_TIMING_DELAY: return JobServerProtoEnums.CONSTRAINT_TIMING_DELAY; case CONSTRAINT_WITHIN_QUOTA: diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java index 2a2d602b24bf..8453e53782ca 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java @@ -26,6 +26,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobSchedulerService.Constants; import com.android.server.job.StateChangedListener; @@ -165,6 +166,15 @@ public abstract class StateController { return mService.areComponentsInPlaceLocked(jobStatus); } + protected void logDeviceWideConstraintStateToStatsd(int constraint, boolean satisfied) { + FrameworkStatsLog.write( + FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED, + JobStatus.getProtoConstraint(constraint), + satisfied + ? FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED + : FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED); + } + public abstract void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate); public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, diff --git a/core/api/test-current.txt b/core/api/test-current.txt index d18a9c7da5a1..93c0c4d7a024 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1155,9 +1155,11 @@ package android.hardware.camera2 { package android.hardware.devicestate { public final class DeviceStateManager { + method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelBaseStateOverride(); method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelStateRequest(); method @NonNull public int[] getSupportedStates(); method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback); + method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestBaseStateOverride(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback); method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback); method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback); field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff @@ -3404,6 +3406,7 @@ package android.window { method @NonNull public android.window.WindowContainerTransaction createTaskFragment(@NonNull android.window.TaskFragmentCreationParams); method @NonNull public android.window.WindowContainerTransaction deleteTaskFragment(@NonNull android.window.WindowContainerToken); method public int describeContents(); + method @NonNull public android.window.WindowContainerTransaction finishActivity(@NonNull android.os.IBinder); method @NonNull public android.window.WindowContainerTransaction removeTask(@NonNull android.window.WindowContainerToken); method @NonNull public android.window.WindowContainerTransaction reorder(@NonNull android.window.WindowContainerToken, boolean); method @NonNull public android.window.WindowContainerTransaction reparent(@NonNull android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, boolean); diff --git a/core/java/Android.bp b/core/java/Android.bp index a7d4342be642..77589a213e17 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -15,6 +15,14 @@ filegroup { "**/*.java", "**/*.aidl", ], + exclude_srcs: [ + // Remove election toolbar code from build time + "android/service/selectiontoolbar/*.aidl", + "android/service/selectiontoolbar/*.java", + "android/view/selectiontoolbar/*.aidl", + "android/view/selectiontoolbar/*.java", + "com/android/internal/widget/floatingtoolbar/RemoteFloatingToolbarPopup.java", + ], visibility: ["//frameworks/base"], } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 6615374f71ec..b6189692107e 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -29,8 +29,6 @@ import android.app.ambientcontext.AmbientContextManager; import android.app.ambientcontext.IAmbientContextManager; import android.app.appsearch.AppSearchManagerFrameworkInitializer; import android.app.blob.BlobStoreManagerFrameworkInitializer; -import android.app.cloudsearch.CloudSearchManager; -import android.app.cloudsearch.ICloudSearchManager; import android.app.contentsuggestions.ContentSuggestionsManager; import android.app.contentsuggestions.IContentSuggestionsManager; import android.app.job.JobSchedulerFrameworkInitializer; @@ -230,8 +228,6 @@ import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.IContentCaptureManager; import android.view.displayhash.DisplayHashManager; import android.view.inputmethod.InputMethodManager; -import android.view.selectiontoolbar.ISelectionToolbarManager; -import android.view.selectiontoolbar.SelectionToolbarManager; import android.view.textclassifier.TextClassificationManager; import android.view.textservice.TextServicesManager; import android.view.translation.ITranslationManager; @@ -365,15 +361,6 @@ public final class SystemServiceRegistry { return new TextClassificationManager(ctx); }}); - registerService(Context.SELECTION_TOOLBAR_SERVICE, SelectionToolbarManager.class, - new CachedServiceFetcher<SelectionToolbarManager>() { - @Override - public SelectionToolbarManager createService(ContextImpl ctx) { - IBinder b = ServiceManager.getService(Context.SELECTION_TOOLBAR_SERVICE); - return new SelectionToolbarManager(ctx.getOuterContext(), - ISelectionToolbarManager.Stub.asInterface(b)); - }}); - registerService(Context.FONT_SERVICE, FontManager.class, new CachedServiceFetcher<FontManager>() { @Override @@ -1231,17 +1218,6 @@ public final class SystemServiceRegistry { } }); - registerService(Context.CLOUDSEARCH_SERVICE, CloudSearchManager.class, - new CachedServiceFetcher<CloudSearchManager>() { - @Override - public CloudSearchManager createService(ContextImpl ctx) - throws ServiceNotFoundException { - IBinder b = ServiceManager.getService(Context.CLOUDSEARCH_SERVICE); - return b == null ? null : - new CloudSearchManager(ICloudSearchManager.Stub.asInterface(b)); - } - }); - registerService(Context.APP_PREDICTION_SERVICE, AppPredictionManager.class, new CachedServiceFetcher<AppPredictionManager>() { @Override diff --git a/core/java/android/app/cloudsearch/CloudSearchManager.java b/core/java/android/app/cloudsearch/CloudSearchManager.java index 471e423db458..b7bbf4712842 100644 --- a/core/java/android/app/cloudsearch/CloudSearchManager.java +++ b/core/java/android/app/cloudsearch/CloudSearchManager.java @@ -15,17 +15,15 @@ */ package android.app.cloudsearch; -import static java.util.Objects.requireNonNull; - import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; -import android.os.RemoteException; import java.util.concurrent.Executor; + /** * A {@link CloudSearchManager} is the class having all the information passed to search providers. * @@ -41,7 +39,7 @@ public class CloudSearchManager { /** * Invoked by receiving app with the result of the search. * - * @param request original request for the search. + * @param request original request for the search. * @param response search result. */ void onSearchSucceeded(@NonNull SearchRequest request, @NonNull SearchResponse response); @@ -51,17 +49,15 @@ public class CloudSearchManager { * Each failure is recorded. The client may receive a failure from one provider and * subsequently receive successful searches from other providers * - * @param request original request for the search. + * @param request original request for the search. * @param response search result. */ void onSearchFailed(@NonNull SearchRequest request, @NonNull SearchResponse response); } - private final ICloudSearchManager mService; - /** @hide **/ - public CloudSearchManager(@NonNull ICloudSearchManager service) { - mService = service; + public CloudSearchManager() { + } /** @@ -69,10 +65,9 @@ public class CloudSearchManager { * to the designated cloud lookup services. After the lookup is done, the given * callback will be invoked by the system with the result or lack thereof. * - * @param request request to be searched. + * @param request request to be searched. * @param callbackExecutor where the callback is invoked. - * @param callback invoked when the result is available. - * + * @param callback invoked when the result is available. * @hide */ @SystemApi @@ -80,49 +75,8 @@ public class CloudSearchManager { public void search(@NonNull SearchRequest request, @NonNull @CallbackExecutor Executor callbackExecutor, @NonNull CallBack callback) { - try { - mService.search( - requireNonNull(request), - new CallBackWrapper( - requireNonNull(request), - requireNonNull(callback), - requireNonNull(callbackExecutor))); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - private final class CallBackWrapper extends - ICloudSearchManagerCallback.Stub { - @NonNull - private final SearchRequest mSearchRequest; - - @NonNull - private final CallBack mCallback; - - @NonNull - private final Executor mCallbackExecutor; - - CallBackWrapper( - SearchRequest searchRequest, - CallBack callback, - Executor callbackExecutor) { - mSearchRequest = searchRequest; - mCallback = callback; - mCallbackExecutor = callbackExecutor; - } - - - @Override - public void onSearchSucceeded(SearchResponse searchResponse) { - mCallbackExecutor.execute( - () -> mCallback.onSearchSucceeded(mSearchRequest, searchResponse)); - } - - @Override - public void onSearchFailed(SearchResponse searchResponse) { - mCallbackExecutor.execute( - () -> mCallback.onSearchFailed(mSearchRequest, searchResponse)); - } + callbackExecutor.execute( + () -> callback.onSearchFailed(request, + new SearchResponse.Builder(SearchResponse.SEARCH_STATUS_UNKNOWN).build())); } } diff --git a/core/java/android/app/cloudsearch/ICloudSearchManager.aidl b/core/java/android/app/cloudsearch/ICloudSearchManager.aidl deleted file mode 100644 index 18f8fc476191..000000000000 --- a/core/java/android/app/cloudsearch/ICloudSearchManager.aidl +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2022, 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 android.app.cloudsearch; - -import android.app.cloudsearch.SearchRequest; -import android.app.cloudsearch.SearchResponse; -import android.app.cloudsearch.ICloudSearchManagerCallback; - -/** - * Used by {@link CloudSearchManager} to tell system server to do search. - * - * @hide - */ -oneway interface ICloudSearchManager { - void search(in SearchRequest request, in ICloudSearchManagerCallback callBack); - - void returnResults(in IBinder token, in String requestId, - in SearchResponse response); -} diff --git a/core/java/android/app/cloudsearch/ICloudSearchManagerCallback.aidl b/core/java/android/app/cloudsearch/ICloudSearchManagerCallback.aidl deleted file mode 100644 index 84771dd4a19b..000000000000 --- a/core/java/android/app/cloudsearch/ICloudSearchManagerCallback.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2022, 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 android.app.cloudsearch; - -import android.app.cloudsearch.SearchResponse; - - -/** - * Callback used by system server to notify invoker of {@link CloudSearchManager} of the result - * - * @hide - */ -oneway interface ICloudSearchManagerCallback { - void onSearchSucceeded(in SearchResponse response); - - void onSearchFailed(in SearchResponse response); -} diff --git a/core/java/android/app/cloudsearch/SearchRequest.java b/core/java/android/app/cloudsearch/SearchRequest.java index bf783255b3d9..3725b36a1c7f 100644 --- a/core/java/android/app/cloudsearch/SearchRequest.java +++ b/core/java/android/app/cloudsearch/SearchRequest.java @@ -15,8 +15,6 @@ */ package android.app.cloudsearch; -import static java.util.Objects.requireNonNull; - import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; @@ -28,7 +26,6 @@ import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Objects; /** * A {@link SearchRequest} is the data class having all the information passed to search providers. @@ -39,36 +36,6 @@ import java.util.Objects; public final class SearchRequest implements Parcelable { /** - * Query for search. - */ - @NonNull - private final String mQuery; - - /** - * Expected result offset for pagination. - * - * The default value is 0. - */ - private final int mResultOffset; - - /** - * Expected search result number. - * - * The default value is 10. - */ - private final int mResultNumber; - - /** - * The max acceptable latency. - * - * The default value is 200 milliseconds. - */ - private final float mMaxLatencyMillis; - - @Nullable - private String mId = null; - - /** * List of public static KEYS for the Bundle to mSearchConstraints. mSearchConstraints * contains various constraints specifying the search intent. * @@ -76,121 +43,83 @@ public final class SearchRequest implements Parcelable { */ @Retention(RetentionPolicy.SOURCE) @StringDef(prefix = {"CONSTRAINT_"}, - value = {CONSTRAINT_IS_PRESUBMIT_SUGGESTION, - CONSTRAINT_SEARCH_PROVIDER_FILTER}) - public @interface SearchConstraintKey {} - /** If this is a presubmit suggestion, Boolean value expected. - * presubmit is the input before the user finishes the entire query, i.e. push "ENTER" or - * "SEARCH" button. After the user finishes the entire query, the behavior is postsubmit. + value = {CONSTRAINT_IS_PRESUBMIT_SUGGESTION, + CONSTRAINT_SEARCH_PROVIDER_FILTER}) + public @interface SearchConstraintKey { + } + + /** + * If this is a presubmit suggestion, Boolean value expected. + * presubmit is the input before the user finishes the entire query, i.e. push "ENTER" or + * "SEARCH" button. After the user finishes the entire query, the behavior is postsubmit. */ public static final String CONSTRAINT_IS_PRESUBMIT_SUGGESTION = "android.app.cloudsearch.IS_PRESUBMIT_SUGGESTION"; - /** The target search provider list of package names(separated by ;), String value expected. + /** + * The target search provider list of package names(separated by ;), String value expected. * If this is not provided or its value is empty, then no filter will be applied. */ public static final String CONSTRAINT_SEARCH_PROVIDER_FILTER = "android.app.cloudsearch.SEARCH_PROVIDER_FILTER"; - @NonNull - private Bundle mSearchConstraints; - - /** Auto set by system servier, and the caller cannot set it. - * - * The caller's package name. - * - */ - @NonNull - private String mCallerPackageName; - - private SearchRequest(Parcel in) { - this.mQuery = in.readString(); - this.mResultOffset = in.readInt(); - this.mResultNumber = in.readInt(); - this.mMaxLatencyMillis = in.readFloat(); - this.mSearchConstraints = in.readBundle(); - this.mId = in.readString(); - this.mCallerPackageName = in.readString(); - } - - private SearchRequest(String query, int resultOffset, int resultNumber, float maxLatencyMillis, - Bundle searchConstraints, String callerPackageName) { - mQuery = query; - mResultOffset = resultOffset; - mResultNumber = resultNumber; - mMaxLatencyMillis = maxLatencyMillis; - mSearchConstraints = searchConstraints; - mCallerPackageName = callerPackageName; + private SearchRequest() { } /** Returns the original query. */ @NonNull public String getQuery() { - return mQuery; + return ""; } /** Returns the result offset. */ public int getResultOffset() { - return mResultOffset; + return 0; } /** Returns the expected number of search results. */ public int getResultNumber() { - return mResultNumber; + return 0; } /** Returns the maximum latency requirement. */ public float getMaxLatencyMillis() { - return mMaxLatencyMillis; + return 0; } /** Returns the search constraints. */ @NonNull public Bundle getSearchConstraints() { - return mSearchConstraints; + return Bundle.EMPTY; } /** Gets the caller's package name. */ @NonNull public String getCallerPackageName() { - return mCallerPackageName; + return ""; } /** Returns the search request id, which is used to identify the request. */ @NonNull public String getRequestId() { - if (mId == null || mId.length() == 0) { - mId = String.valueOf(toString().hashCode()); - } - - return mId; + return ""; } - /** Sets the caller, and this will be set by the system server. + /** + * Sets the caller, and this will be set by the system server. * * @hide */ public void setCallerPackageName(@NonNull String callerPackageName) { - this.mCallerPackageName = callerPackageName; - } - - private SearchRequest(Builder b) { - mQuery = requireNonNull(b.mQuery); - mResultOffset = b.mResultOffset; - mResultNumber = b.mResultNumber; - mMaxLatencyMillis = b.mMaxLatencyMillis; - mSearchConstraints = requireNonNull(b.mSearchConstraints); - mCallerPackageName = requireNonNull(b.mCallerPackageName); } /** * @see Creator - * */ @NonNull public static final Creator<SearchRequest> CREATOR = new Creator<SearchRequest>() { @Override public SearchRequest createFromParcel(Parcel p) { - return new SearchRequest(p); + return new SearchRequest(); } @Override @@ -201,13 +130,6 @@ public final class SearchRequest implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(this.mQuery); - dest.writeInt(this.mResultOffset); - dest.writeInt(this.mResultNumber); - dest.writeFloat(this.mMaxLatencyMillis); - dest.writeBundle(this.mSearchConstraints); - dest.writeString(getRequestId()); - dest.writeString(this.mCallerPackageName); } @Override @@ -217,44 +139,17 @@ public final class SearchRequest implements Parcelable { @Override public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (obj == null || getClass() != obj.getClass()) { - return false; - } - - SearchRequest that = (SearchRequest) obj; - return Objects.equals(mQuery, that.mQuery) - && mResultOffset == that.mResultOffset - && mResultNumber == that.mResultNumber - && mMaxLatencyMillis == that.mMaxLatencyMillis - && Objects.equals(mSearchConstraints, that.mSearchConstraints) - && Objects.equals(mCallerPackageName, that.mCallerPackageName); + return false; } @Override public String toString() { - boolean isPresubmit = - mSearchConstraints.containsKey(CONSTRAINT_IS_PRESUBMIT_SUGGESTION) - && mSearchConstraints.getBoolean(CONSTRAINT_IS_PRESUBMIT_SUGGESTION); - - String searchProvider = "EMPTY"; - if (mSearchConstraints.containsKey(CONSTRAINT_SEARCH_PROVIDER_FILTER)) { - searchProvider = mSearchConstraints.getString(CONSTRAINT_SEARCH_PROVIDER_FILTER); - } - - return String.format("SearchRequest: {query:%s,offset:%d;number:%d;max_latency:%f;" - + "is_presubmit:%b;search_provider:%s;callerPackageName:%s}", mQuery, - mResultOffset, mResultNumber, mMaxLatencyMillis, isPresubmit, searchProvider, - mCallerPackageName); + return ""; } @Override public int hashCode() { - return Objects.hash(mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis, - mSearchConstraints, mCallerPackageName); + return 0; } /** @@ -264,87 +159,62 @@ public final class SearchRequest implements Parcelable { */ @SystemApi public static final class Builder { - private String mQuery; - private int mResultOffset; - private int mResultNumber; - private float mMaxLatencyMillis; - private Bundle mSearchConstraints; - private String mCallerPackageName; - /** - * * @param query the query for search. - * * @hide */ @SystemApi public Builder(@NonNull String query) { - mQuery = query; - - mResultOffset = 0; - mResultNumber = 10; - mMaxLatencyMillis = 200; - mSearchConstraints = Bundle.EMPTY; - mCallerPackageName = "DEFAULT_CALLER"; } /** Sets the input query. */ @NonNull public Builder setQuery(@NonNull String query) { - this.mQuery = query; return this; } /** Sets the search result offset. */ @NonNull public Builder setResultOffset(int resultOffset) { - this.mResultOffset = resultOffset; return this; } /** Sets the expected number of search result. */ @NonNull public Builder setResultNumber(int resultNumber) { - this.mResultNumber = resultNumber; return this; } /** Sets the maximum acceptable search latency. */ @NonNull public Builder setMaxLatencyMillis(float maxLatencyMillis) { - this.mMaxLatencyMillis = maxLatencyMillis; return this; } - /** Sets the search constraints, such as the user location, the search type(presubmit or - * postsubmit), and the target search providers. */ + /** + * Sets the search constraints, such as the user location, the search type(presubmit or + * postsubmit), and the target search providers. + */ @NonNull public Builder setSearchConstraints(@Nullable Bundle searchConstraints) { - this.mSearchConstraints = searchConstraints; return this; } - /** Sets the caller, and this will be set by the system server. + /** + * Sets the caller, and this will be set by the system server. * * @hide */ @NonNull @TestApi public Builder setCallerPackageName(@NonNull String callerPackageName) { - this.mCallerPackageName = callerPackageName; return this; } /** Builds a SearchRequest based-on the given params. */ @NonNull public SearchRequest build() { - if (mQuery == null || mResultOffset < 0 || mResultNumber < 1 || mMaxLatencyMillis < 0 - || mSearchConstraints == null) { - throw new IllegalStateException("Please make sure all required args are valid."); - } - - return new SearchRequest(mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis, - mSearchConstraints, mCallerPackageName); + return new SearchRequest(); } } } diff --git a/core/java/android/app/cloudsearch/SearchResponse.java b/core/java/android/app/cloudsearch/SearchResponse.java index 607bd561d331..c86142e0d22d 100644 --- a/core/java/android/app/cloudsearch/SearchResponse.java +++ b/core/java/android/app/cloudsearch/SearchResponse.java @@ -15,8 +15,6 @@ */ package android.app.cloudsearch; -import static java.util.Objects.requireNonNull; - import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; @@ -25,7 +23,6 @@ import android.os.Parcelable; import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * A {@link SearchResponse} includes search results and associated meta information. @@ -37,77 +34,53 @@ public final class SearchResponse implements Parcelable { /** @hide */ @IntDef(prefix = {"SEARCH_STATUS_"}, value = {SEARCH_STATUS_UNKNOWN, - SEARCH_STATUS_OK, - SEARCH_STATUS_TIME_OUT, - SEARCH_STATUS_NO_INTERNET}) - public @interface SearchStatusCode {} + SEARCH_STATUS_OK, + SEARCH_STATUS_TIME_OUT, + SEARCH_STATUS_NO_INTERNET}) + public @interface SearchStatusCode { + } + public static final int SEARCH_STATUS_UNKNOWN = -1; public static final int SEARCH_STATUS_OK = 0; public static final int SEARCH_STATUS_TIME_OUT = 1; public static final int SEARCH_STATUS_NO_INTERNET = 2; - private final int mStatusCode; - - /** Auto set by system servier, and the provider cannot set it. */ - @NonNull - private String mSource; - - @NonNull - private final List<SearchResult> mSearchResults; - - private SearchResponse(Parcel in) { - this.mStatusCode = in.readInt(); - this.mSource = in.readString(); - this.mSearchResults = in.createTypedArrayList(SearchResult.CREATOR); - } - - private SearchResponse(@SearchStatusCode int statusCode, String source, - List<SearchResult> searchResults) { - mStatusCode = statusCode; - mSource = source; - mSearchResults = searchResults; + private SearchResponse() { } /** Gets the search status code. */ public int getStatusCode() { - return mStatusCode; + return SEARCH_STATUS_UNKNOWN; } /** Gets the search provider package name. */ @NonNull public String getSource() { - return mSource; + return ""; } /** Gets the search results, which can be empty. */ @NonNull public List<SearchResult> getSearchResults() { - return mSearchResults; + return new ArrayList<SearchResult>(); } - /** Sets the search provider, and this will be set by the system server. + /** + * Sets the search provider, and this will be set by the system server. * * @hide */ public void setSource(@NonNull String source) { - this.mSource = source; - } - - private SearchResponse(Builder b) { - mStatusCode = b.mStatusCode; - mSource = requireNonNull(b.mSource); - mSearchResults = requireNonNull(b.mSearchResults); } /** - * * @see Creator - * */ - @NonNull public static final Creator<SearchResponse> CREATOR = new Creator<SearchResponse>() { + @NonNull + public static final Creator<SearchResponse> CREATOR = new Creator<SearchResponse>() { @Override public SearchResponse createFromParcel(Parcel p) { - return new SearchResponse(p); + return new SearchResponse(); } @Override @@ -118,9 +91,6 @@ public final class SearchResponse implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(this.mStatusCode); - dest.writeString(this.mSource); - dest.writeTypedList(this.mSearchResults); } @Override @@ -130,23 +100,12 @@ public final class SearchResponse implements Parcelable { @Override public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (obj == null || getClass() != obj.getClass()) { - return false; - } - - SearchResponse that = (SearchResponse) obj; - return mStatusCode == that.mStatusCode - && Objects.equals(mSource, that.mSource) - && Objects.equals(mSearchResults, that.mSearchResults); + return false; } @Override public int hashCode() { - return Objects.hash(mStatusCode, mSource, mSearchResults); + return 0; } /** @@ -156,59 +115,40 @@ public final class SearchResponse implements Parcelable { */ @SystemApi public static final class Builder { - private int mStatusCode; - private String mSource; - private List<SearchResult> mSearchResults; - /** - * * @param statusCode the search status code. - * * @hide */ @SystemApi public Builder(@SearchStatusCode int statusCode) { - mStatusCode = statusCode; - - /** Init with a default value. */ - mSource = "DEFAULT"; - - mSearchResults = new ArrayList<SearchResult>(); } /** Sets the search status code. */ @NonNull public Builder setStatusCode(@SearchStatusCode int statusCode) { - this.mStatusCode = statusCode; return this; } - /** Sets the search provider, and this will be set by the system server. + /** + * Sets the search provider, and this will be set by the system server. * * @hide */ @NonNull public Builder setSource(@NonNull String source) { - this.mSource = source; return this; } /** Sets the search results. */ @NonNull public Builder setSearchResults(@NonNull List<SearchResult> searchResults) { - this.mSearchResults = searchResults; return this; } /** Builds a SearchResponse based-on the given parameters. */ @NonNull public SearchResponse build() { - if (mStatusCode < SEARCH_STATUS_UNKNOWN || mStatusCode > SEARCH_STATUS_NO_INTERNET - || mSearchResults == null) { - throw new IllegalStateException("Please make sure all @NonNull args are assigned."); - } - - return new SearchResponse(mStatusCode, mSource, mSearchResults); + return new SearchResponse(); } } } diff --git a/core/java/android/app/cloudsearch/SearchResult.java b/core/java/android/app/cloudsearch/SearchResult.java index c6583b65f9c2..123c3a2a5f48 100644 --- a/core/java/android/app/cloudsearch/SearchResult.java +++ b/core/java/android/app/cloudsearch/SearchResult.java @@ -15,8 +15,6 @@ */ package android.app.cloudsearch; -import static java.util.Objects.requireNonNull; - import android.annotation.NonNull; import android.annotation.StringDef; import android.annotation.SuppressLint; @@ -27,7 +25,6 @@ import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Objects; /** * A {@link SearchResult} includes all the information for one result item. @@ -37,17 +34,6 @@ import java.util.Objects; @SystemApi public final class SearchResult implements Parcelable { - /** Short content best describing the result item. */ - @NonNull - private final String mTitle; - - /** Matched contents in the result item. */ - @NonNull - private final String mSnippet; - - /** Ranking Score provided by the search provider. */ - private final float mScore; - /** * List of public static KEYS for Bundles in mExtraInfos. * mExtraInfos contains various information specified for different data types. @@ -56,28 +42,30 @@ public final class SearchResult implements Parcelable { */ @Retention(RetentionPolicy.SOURCE) @StringDef(prefix = {"EXTRAINFO_"}, - value = {EXTRAINFO_APP_DOMAIN_URL, - EXTRAINFO_APP_ICON, - EXTRAINFO_APP_DEVELOPER_NAME, - EXTRAINFO_APP_SIZE_BYTES, - EXTRAINFO_APP_STAR_RATING, - EXTRAINFO_APP_IARC, - EXTRAINFO_APP_REVIEW_COUNT, - EXTRAINFO_APP_CONTAINS_ADS_DISCLAIMER, - EXTRAINFO_APP_CONTAINS_IAP_DISCLAIMER, - EXTRAINFO_SHORT_DESCRIPTION, - EXTRAINFO_LONG_DESCRIPTION, - EXTRAINFO_SCREENSHOTS, - EXTRAINFO_APP_BADGES, - EXTRAINFO_ACTION_BUTTON_TEXT_PREREGISTERING, - EXTRAINFO_ACTION_BUTTON_IMAGE_PREREGISTERING, - EXTRAINFO_ACTION_APP_CARD, - EXTRAINFO_ACTION_INSTALL_BUTTON, - EXTRAINFO_APP_PACKAGE_NAME, - EXTRAINFO_APP_INSTALL_COUNT, - EXTRAINFO_WEB_URL, - EXTRAINFO_WEB_ICON}) - public @interface SearchResultExtraInfoKey {} + value = {EXTRAINFO_APP_DOMAIN_URL, + EXTRAINFO_APP_ICON, + EXTRAINFO_APP_DEVELOPER_NAME, + EXTRAINFO_APP_SIZE_BYTES, + EXTRAINFO_APP_STAR_RATING, + EXTRAINFO_APP_IARC, + EXTRAINFO_APP_REVIEW_COUNT, + EXTRAINFO_APP_CONTAINS_ADS_DISCLAIMER, + EXTRAINFO_APP_CONTAINS_IAP_DISCLAIMER, + EXTRAINFO_SHORT_DESCRIPTION, + EXTRAINFO_LONG_DESCRIPTION, + EXTRAINFO_SCREENSHOTS, + EXTRAINFO_APP_BADGES, + EXTRAINFO_ACTION_BUTTON_TEXT_PREREGISTERING, + EXTRAINFO_ACTION_BUTTON_IMAGE_PREREGISTERING, + EXTRAINFO_ACTION_APP_CARD, + EXTRAINFO_ACTION_INSTALL_BUTTON, + EXTRAINFO_APP_PACKAGE_NAME, + EXTRAINFO_APP_INSTALL_COUNT, + EXTRAINFO_WEB_URL, + EXTRAINFO_WEB_ICON}) + public @interface SearchResultExtraInfoKey { + } + /** This App developer website's domain URL, String value expected. */ public static final String EXTRAINFO_APP_DOMAIN_URL = "android.app.cloudsearch.APP_DOMAIN_URL"; /** This App icon, android.graphics.drawable.Icon expected. */ @@ -90,7 +78,8 @@ public final class SearchResult implements Parcelable { /** This App developer's name, Double value expected. */ public static final String EXTRAINFO_APP_STAR_RATING = "android.app.cloudsearch.APP_STAR_RATING"; - /** This App's IARC rating, String value expected. + /** + * This App's IARC rating, String value expected. * IARC (International Age Rating Coalition) is partnered globally with major * content rating organizations to provide a centralized and one-stop-shop for * rating content on a global scale. @@ -142,62 +131,40 @@ public final class SearchResult implements Parcelable { /** Web content's domain icon, android.graphics.drawable.Icon expected. */ public static final String EXTRAINFO_WEB_ICON = "android.app.cloudsearch.WEB_ICON"; - @NonNull - private Bundle mExtraInfos; - - private SearchResult(Parcel in) { - this.mTitle = in.readString(); - this.mSnippet = in.readString(); - this.mScore = in.readFloat(); - this.mExtraInfos = in.readBundle(); - } - - private SearchResult(String title, String snippet, float score, Bundle extraInfos) { - mTitle = title; - mSnippet = snippet; - mScore = score; - mExtraInfos = extraInfos; + private SearchResult() { } /** Gets the search result title. */ @NonNull public String getTitle() { - return mTitle; + return ""; } /** Gets the search result snippet. */ @NonNull public String getSnippet() { - return mSnippet; + return ""; } /** Gets the ranking score provided by the original search provider. */ public float getScore() { - return mScore; + return 0; } /** Gets the extra information associated with the search result. */ @NonNull public Bundle getExtraInfos() { - return mExtraInfos; - } - - private SearchResult(Builder b) { - mTitle = requireNonNull(b.mTitle); - mSnippet = requireNonNull(b.mSnippet); - mScore = b.mScore; - mExtraInfos = requireNonNull(b.mExtraInfos); + return Bundle.EMPTY; } /** - * * @see Creator - * */ - @NonNull public static final Creator<SearchResult> CREATOR = new Creator<SearchResult>() { + @NonNull + public static final Creator<SearchResult> CREATOR = new Creator<SearchResult>() { @Override public SearchResult createFromParcel(Parcel p) { - return new SearchResult(p); + return new SearchResult(); } @Override @@ -208,10 +175,6 @@ public final class SearchResult implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(this.mTitle); - dest.writeString(this.mSnippet); - dest.writeFloat(this.mScore); - dest.writeBundle(this.mExtraInfos); } @Override @@ -221,24 +184,12 @@ public final class SearchResult implements Parcelable { @Override public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (obj == null || getClass() != obj.getClass()) { - return false; - } - - SearchResult that = (SearchResult) obj; - return Objects.equals(mTitle, that.mTitle) - && Objects.equals(mSnippet, that.mSnippet) - && mScore == that.mScore - && Objects.equals(mExtraInfos, that.mExtraInfos); + return false; } @Override public int hashCode() { - return Objects.hash(mTitle, mSnippet, mScore, mExtraInfos); + return 0; } /** @@ -248,63 +199,43 @@ public final class SearchResult implements Parcelable { */ @SystemApi public static final class Builder { - private String mTitle; - private String mSnippet; - private float mScore; - private Bundle mExtraInfos; - /** - * - * @param title the title to the search result. + * @param title the title to the search result. * @param extraInfos the extra infos associated with the search result. - * * @hide */ @SystemApi public Builder(@NonNull String title, @NonNull Bundle extraInfos) { - mTitle = title; - mExtraInfos = extraInfos; - - mSnippet = ""; - mScore = 0; } /** Sets the title to the search result. */ @NonNull public Builder setTitle(@NonNull String title) { - this.mTitle = title; return this; } /** Sets the snippet to the search result. */ @NonNull public Builder setSnippet(@NonNull String snippet) { - this.mSnippet = snippet; return this; } /** Sets the ranking score to the search result. */ @NonNull public Builder setScore(float score) { - this.mScore = score; return this; } /** Adds extra information to the search result for rendering in the UI. */ @NonNull public Builder setExtraInfos(@NonNull Bundle extraInfos) { - this.mExtraInfos = extraInfos; return this; } /** Builds a SearchResult based-on the given parameters. */ @NonNull public SearchResult build() { - if (mTitle == null || mExtraInfos == null || mSnippet == null) { - throw new IllegalStateException("Please make sure all required args are assigned."); - } - - return new SearchResult(mTitle, mSnippet, mScore, mExtraInfos); + return new SearchResult(); } } } diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java index 30aa4db938da..bdd45e6df448 100644 --- a/core/java/android/hardware/devicestate/DeviceStateManager.java +++ b/core/java/android/hardware/devicestate/DeviceStateManager.java @@ -16,6 +16,7 @@ package android.hardware.devicestate; +import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; @@ -115,6 +116,52 @@ public final class DeviceStateManager { } /** + * Submits a {@link DeviceStateRequest request} to override the base state of the device. This + * should only be used for testing, where you want to simulate the physical change to the + * device state. + * <p> + * By default, the request is kept active until one of the following occurs: + * <ul> + * <li>The physical state of the device changes</li> + * <li>The system deems the request can no longer be honored, for example if the requested + * state becomes unsupported. + * <li>A call to {@link #cancelBaseStateOverride}. + * <li>Another processes submits a request succeeding this request in which case the request + * will be canceled. + * </ul> + * + * Submitting a base state override request may not cause any change in the presentation + * of the system if there is an emulated request made through {@link #requestState}, as the + * emulated override requests take priority. + * + * @throws IllegalArgumentException if the requested state is unsupported. + * @throws SecurityException if the caller does not hold the + * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission. + * + * @see DeviceStateRequest + */ + @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) + public void requestBaseStateOverride(@NonNull DeviceStateRequest request, + @Nullable @CallbackExecutor Executor executor, + @Nullable DeviceStateRequest.Callback callback) { + mGlobal.requestBaseStateOverride(request, executor, callback); + } + + /** + * Cancels the active {@link DeviceStateRequest} previously submitted with a call to + * {@link #requestBaseStateOverride(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}. + * <p> + * This method is noop if there is no base state request currently active. + * + * @throws SecurityException if the caller does not hold the + * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission. + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_STATE) + public void cancelBaseStateOverride() { + mGlobal.cancelBaseStateOverride(); + } + + /** * Registers a callback to receive notifications about changes in device state. * * @param executor the executor to process notifications. diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java index aba538f51043..738045dafdf1 100644 --- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java +++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java @@ -18,6 +18,7 @@ package android.hardware.devicestate; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.content.Context; import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; import android.os.Binder; @@ -81,6 +82,7 @@ public final class DeviceStateManagerGlobal { @VisibleForTesting public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) { mDeviceStateManager = deviceStateManager; + registerCallbackIfNeededLocked(); } /** @@ -116,27 +118,22 @@ public final class DeviceStateManagerGlobal { * DeviceStateRequest.Callback) * @see DeviceStateRequest */ + @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE, + conditional = true) public void requestState(@NonNull DeviceStateRequest request, @Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) { - if (callback == null && executor != null) { - throw new IllegalArgumentException("Callback must be supplied with executor."); - } else if (executor == null && callback != null) { - throw new IllegalArgumentException("Executor must be supplied with callback."); - } - + DeviceStateRequestWrapper requestWrapper = new DeviceStateRequestWrapper(request, callback, + executor); synchronized (mLock) { - registerCallbackIfNeededLocked(); - if (findRequestTokenLocked(request) != null) { // This request has already been submitted. return; } - // Add the request wrapper to the mRequests array before requesting the state as the // callback could be triggered immediately if the mDeviceStateManager IBinder is in the // same process as this instance. IBinder token = new Binder(); - mRequests.put(token, new DeviceStateRequestWrapper(request, callback, executor)); + mRequests.put(token, requestWrapper); try { mDeviceStateManager.requestState(token, request.getState(), request.getFlags()); @@ -153,10 +150,10 @@ public final class DeviceStateManagerGlobal { * * @see DeviceStateManager#cancelStateRequest */ + @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE, + conditional = true) public void cancelStateRequest() { synchronized (mLock) { - registerCallbackIfNeededLocked(); - try { mDeviceStateManager.cancelStateRequest(); } catch (RemoteException ex) { @@ -166,6 +163,56 @@ public final class DeviceStateManagerGlobal { } /** + * Submits a {@link DeviceStateRequest request} to modify the base state of the device. + * + * @see DeviceStateManager#requestBaseStateOverride(DeviceStateRequest, Executor, + * DeviceStateRequest.Callback) + * @see DeviceStateRequest + */ + @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) + public void requestBaseStateOverride(@NonNull DeviceStateRequest request, + @Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) { + DeviceStateRequestWrapper requestWrapper = new DeviceStateRequestWrapper(request, callback, + executor); + synchronized (mLock) { + if (findRequestTokenLocked(request) != null) { + // This request has already been submitted. + return; + } + // Add the request wrapper to the mRequests array before requesting the state as the + // callback could be triggered immediately if the mDeviceStateManager IBinder is in the + // same process as this instance. + IBinder token = new Binder(); + mRequests.put(token, requestWrapper); + + try { + mDeviceStateManager.requestBaseStateOverride(token, request.getState(), + request.getFlags()); + } catch (RemoteException ex) { + mRequests.remove(token); + throw ex.rethrowFromSystemServer(); + } + } + } + + /** + * Cancels a {@link DeviceStateRequest request} previously submitted with a call to + * {@link #requestBaseStateOverride(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}. + * + * @see DeviceStateManager#cancelBaseStateOverride + */ + @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) + public void cancelBaseStateOverride() { + synchronized (mLock) { + try { + mDeviceStateManager.cancelBaseStateOverride(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + } + + /** * Registers a callback to receive notifications about changes in device state. * * @see DeviceStateManager#registerCallback(Executor, DeviceStateCallback) @@ -179,9 +226,6 @@ public final class DeviceStateManagerGlobal { // This callback is already registered. return; } - - registerCallbackIfNeededLocked(); - // Add the callback wrapper to the mCallbacks array after registering the callback as // the callback could be triggered immediately if the mDeviceStateManager IBinder is in // the same process as this instance. @@ -357,6 +401,8 @@ public final class DeviceStateManagerGlobal { DeviceStateRequestWrapper(@NonNull DeviceStateRequest request, @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) { + validateRequestWrapperParameters(callback, executor); + mRequest = request; mCallback = callback; mExecutor = executor; @@ -377,5 +423,14 @@ public final class DeviceStateManagerGlobal { mExecutor.execute(() -> mCallback.onRequestCanceled(mRequest)); } + + private void validateRequestWrapperParameters( + @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) { + if (callback == null && executor != null) { + throw new IllegalArgumentException("Callback must be supplied with executor."); + } else if (executor == null && callback != null) { + throw new IllegalArgumentException("Executor must be supplied with callback."); + } + } } } diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl index e450e42497a0..7175eae58a26 100644 --- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl +++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl @@ -41,6 +41,10 @@ interface IDeviceStateManager { * previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a * call to this method. * + * Requesting a state does not cancel a base state override made through + * {@link #requestBaseStateOverride}, but will still attempt to put the device into the + * supplied {@code state}. + * * @param token the request token provided * @param state the state of device the request is asking for * @param flags any flags that correspond to the request @@ -50,14 +54,53 @@ interface IDeviceStateManager { * @throws IllegalStateException if the supplied {@code token} has already been registered. * @throws IllegalArgumentException if the supplied {@code state} is not supported. */ + @JavaPassthrough(annotation= + "@android.annotation.RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true)") void requestState(IBinder token, int state, int flags); /** * Cancels the active request previously submitted with a call to - * {@link #requestState(IBinder, int, int)}. + * {@link #requestState(IBinder, int, int)}. Will have no effect on any base state override that + * was previously requested with {@link #requestBaseStateOverride}. * * @throws IllegalStateException if a callback has not yet been registered for the calling * process. */ + @JavaPassthrough(annotation= + "@android.annotation.RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true)") void cancelStateRequest(); + + /** + * Requests that the device's base state be overridden to the supplied {@code state}. A callback + * <b>MUST</b> have been previously registered with + * {@link #registerCallback(IDeviceStateManagerCallback)} before a call to this method. + * + * This method should only be used for testing, when you want to simulate the device physically + * changing states. If you are looking to change device state for a feature, where the system + * should still be aware that the physical state is different than the emulated state, use + * {@link #requestState}. + * + * @param token the request token provided + * @param state the state of device the request is asking for + * @param flags any flags that correspond to the request + * + * @throws IllegalStateException if a callback has not yet been registered for the calling + * process. + * @throws IllegalStateException if the supplied {@code token} has already been registered. + * @throws IllegalArgumentException if the supplied {@code state} is not supported. + */ + @JavaPassthrough(annotation= + "@android.annotation.RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)") + void requestBaseStateOverride(IBinder token, int state, int flags); + + /** + * Cancels the active base state request previously submitted with a call to + * {@link #overrideBaseState(IBinder, int, int)}. + * + * @throws IllegalStateException if a callback has not yet been registered for the calling + * process. + */ + @JavaPassthrough(annotation= + "@android.annotation.RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)") + void cancelBaseStateOverride(); } diff --git a/core/java/android/service/cloudsearch/CloudSearchService.java b/core/java/android/service/cloudsearch/CloudSearchService.java index 5efa1acf8ffa..0ce968939e02 100644 --- a/core/java/android/service/cloudsearch/CloudSearchService.java +++ b/core/java/android/service/cloudsearch/CloudSearchService.java @@ -15,25 +15,14 @@ */ package android.service.cloudsearch; -import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; - import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.SystemApi; import android.app.Service; -import android.app.cloudsearch.ICloudSearchManager; import android.app.cloudsearch.SearchRequest; import android.app.cloudsearch.SearchResponse; -import android.content.Context; import android.content.Intent; -import android.os.Handler; import android.os.IBinder; -import android.os.Looper; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.service.cloudsearch.ICloudSearchService.Stub; -import android.util.Log; -import android.util.Slog; /** * A service for returning search results from cloud services in response to an on device query. @@ -72,39 +61,18 @@ public abstract class CloudSearchService extends Service { */ public static final String SERVICE_INTERFACE = "android.service.cloudsearch.CloudSearchService"; - private static final boolean DEBUG = false; - private static final String TAG = "CloudSearchService"; - private Handler mHandler; - private ICloudSearchManager mService; - - private final android.service.cloudsearch.ICloudSearchService mInterface = new Stub() { - @Override - public void onSearch(SearchRequest request) { - mHandler.sendMessage( - obtainMessage(CloudSearchService::onSearch, - CloudSearchService.this, request)); - } - }; @CallSuper @Override public void onCreate() { super.onCreate(); - if (DEBUG) { - Log.d(TAG, "onCreate CloudSearchService"); - } - mHandler = new Handler(Looper.getMainLooper(), null, true); - - IBinder b = ServiceManager.getService(Context.CLOUDSEARCH_SERVICE); - mService = android.app.cloudsearch.ICloudSearchManager.Stub.asInterface(b); } /** * onSearch receives the input request, retrievals the search provider's own * corpus and returns the search response through returnResults below. * - *@param request the search request passed from the client. - * + * @param request the search request passed from the client. */ public abstract void onSearch(@NonNull SearchRequest request); @@ -112,30 +80,16 @@ public abstract class CloudSearchService extends Service { * returnResults returnes the response and its associated requestId, where * requestIs is generated by request through getRequestId(). * - *@param requestId the request ID got from request.getRequestId(). - *@param response the search response returned from the search provider. - * + * @param requestId the request ID got from request.getRequestId(). + * @param response the search response returned from the search provider. */ public final void returnResults(@NonNull String requestId, - @NonNull SearchResponse response) { - try { - mService.returnResults(mInterface.asBinder(), requestId, response); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + @NonNull SearchResponse response) { } @Override @NonNull public final IBinder onBind(@NonNull Intent intent) { - if (DEBUG) { - Log.d(TAG, "onBind CloudSearchService"); - } - if (SERVICE_INTERFACE.equals(intent.getAction())) { - return mInterface.asBinder(); - } - Slog.w(TAG, "Tried to bind to wrong intent (should be " - + SERVICE_INTERFACE + ": " + intent); return null; } } diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java index 163d6ed4b18b..432444211bce 100644 --- a/core/java/android/service/dreams/DreamOverlayService.java +++ b/core/java/android/service/dreams/DreamOverlayService.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.app.Service; +import android.content.ComponentName; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; @@ -36,6 +37,7 @@ public abstract class DreamOverlayService extends Service { private static final String TAG = "DreamOverlayService"; private static final boolean DEBUG = false; private boolean mShowComplications; + private ComponentName mDreamComponent; private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() { @Override @@ -56,6 +58,8 @@ public abstract class DreamOverlayService extends Service { public final IBinder onBind(@NonNull Intent intent) { mShowComplications = intent.getBooleanExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, DreamService.DEFAULT_SHOW_COMPLICATIONS); + mDreamComponent = intent.getParcelableExtra(DreamService.EXTRA_DREAM_COMPONENT, + ComponentName.class); return mDreamOverlay.asBinder(); } @@ -84,4 +88,12 @@ public abstract class DreamOverlayService extends Service { public final boolean shouldShowComplications() { return mShowComplications; } + + /** + * Returns the active dream component. + * @hide + */ + public final ComponentName getDreamComponent() { + return mDreamComponent; + } } diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 75155383855b..d066ee773006 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -218,6 +218,12 @@ public class DreamService extends Service implements Window.Callback { "android.service.dreams.SHOW_COMPLICATIONS"; /** + * Extra containing the component name for the active dream. + * @hide + */ + public static final String EXTRA_DREAM_COMPONENT = "android.service.dreams.DREAM_COMPONENT"; + + /** * The default value for whether to show complications on the overlay. * @hide */ @@ -271,6 +277,7 @@ public class DreamService extends Service implements Window.Callback { overlayIntent.setComponent(overlayService); overlayIntent.putExtra(EXTRA_SHOW_COMPLICATIONS, fetchShouldShowComplications(context, serviceInfo)); + overlayIntent.putExtra(EXTRA_DREAM_COMPONENT, dreamService); context.bindService(overlayIntent, this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE); diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java index d755d3877275..eac3bee8e790 100644 --- a/core/java/android/service/voice/HotwordDetectionService.java +++ b/core/java/android/service/voice/HotwordDetectionService.java @@ -87,6 +87,14 @@ public abstract class HotwordDetectionService extends Service { public static final int MAXIMUM_NUMBER_OF_INITIALIZATION_STATUS_CUSTOM_ERROR = 2; /** + * Feature flag for Attention Service. + * + * TODO(b/247920386): Add TestApi annotation + * @hide + */ + public static final boolean ENABLE_PROXIMITY_RESULT = false; + + /** * Indicates that the updated status is successful. */ public static final int INITIALIZATION_STATUS_SUCCESS = 0; diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 583252756b92..c8c941a220f0 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -22,6 +22,7 @@ import static android.view.InsetsSourceProto.VISIBLE; import static android.view.InsetsSourceProto.VISIBLE_FRAME; import static android.view.InsetsState.ITYPE_CAPTION_BAR; import static android.view.InsetsState.ITYPE_IME; +import static android.view.ViewRootImpl.CAPTION_ON_SHELL; import android.annotation.NonNull; import android.annotation.Nullable; @@ -148,7 +149,7 @@ public class InsetsSource implements Parcelable { // During drag-move and drag-resizing, the caption insets position may not get updated // before the app frame get updated. To layout the app content correctly during drag events, // we always return the insets with the corresponding height covering the top. - if (getType() == ITYPE_CAPTION_BAR) { + if (!CAPTION_ON_SHELL && getType() == ITYPE_CAPTION_BAR) { return Insets.of(0, frame.height(), 0, 0); } // Checks for whether there is shared edge with insets for 0-width/height window. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index b0d465769deb..a78af04995d3 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -8207,7 +8207,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // We have not been laid out yet, hence cannot evaluate // whether this view is visible to the user, we will do // the evaluation once layout is complete. - if (!isLaidOut()) { + // Sometimes, views are already laid out, but it's still + // not visible to the user, we also do the evaluation once + // the view is visible. ex: There is a fade-in animation + // for the activity, the view will be laid out when the + // animation beginning. On the time, the view is not visible + // to the user. And then as the animation progresses, the view + // becomes visible to the user. + if (!isLaidOut() || !isVisibleToUser()) { mPrivateFlags3 |= PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT; } else if (isVisibleToUser()) { if (isFocused()) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index a78d10aaf1ad..e4caa385bc9b 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -284,7 +284,7 @@ public final class ViewRootImpl implements ViewParent, * @hide */ public static final boolean LOCAL_LAYOUT = - SystemProperties.getBoolean("persist.debug.local_layout", true); + SystemProperties.getBoolean("persist.debug.local_layout", false); /** * Set this system property to true to force the view hierarchy to render diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 33ea2e4af473..641d1a189711 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -125,8 +125,15 @@ public final class TransitionInfo implements Parcelable { /** The container attaches work profile thumbnail for cross profile animation. */ public static final int FLAG_CROSS_PROFILE_WORK_THUMBNAIL = 1 << 13; + /** + * Whether the window is covered by an app starting window. This is different from + * {@link #FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT} which is only set on the Activity window + * that contains the starting window. + */ + public static final int FLAG_IS_BEHIND_STARTING_WINDOW = 1 << 14; + /** The first unused bit. This can be used by remotes to attach custom flags to this change. */ - public static final int FLAG_FIRST_CUSTOM = 1 << 14; + public static final int FLAG_FIRST_CUSTOM = 1 << 15; /** @hide */ @IntDef(prefix = { "FLAG_" }, value = { @@ -145,6 +152,7 @@ public final class TransitionInfo implements Parcelable { FLAG_WILL_IME_SHOWN, FLAG_CROSS_PROFILE_OWNER_THUMBNAIL, FLAG_CROSS_PROFILE_WORK_THUMBNAIL, + FLAG_IS_BEHIND_STARTING_WINDOW, FLAG_FIRST_CUSTOM }) public @interface ChangeFlags {} @@ -351,6 +359,9 @@ public final class TransitionInfo implements Parcelable { if ((flags & FLAG_FILLS_TASK) != 0) { sb.append(sb.length() == 0 ? "" : "|").append("FILLS_TASK"); } + if ((flags & FLAG_IS_BEHIND_STARTING_WINDOW) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("IS_BEHIND_STARTING_WINDOW"); + } if ((flags & FLAG_FIRST_CUSTOM) != 0) { sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM"); } diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index ffbdf08e99dc..cfad1afe1b5b 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -706,6 +706,23 @@ public final class WindowContainerTransaction implements Parcelable { } /** + * Finishes the Activity. + * Comparing to directly calling {@link android.app.Activity#finish()}, calling this can make + * sure the finishing happens in the same transaction with other operations. + * @param activityToken activity to be finished. + */ + @NonNull + public WindowContainerTransaction finishActivity(@NonNull IBinder activityToken) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder( + HierarchyOp.HIERARCHY_OP_TYPE_FINISH_ACTIVITY) + .setContainer(activityToken) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + + /** * Sets/removes the always on top flag for this {@code windowContainer}. See * {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}. * Please note that this method is only intended to be used for a @@ -1163,6 +1180,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT = 18; public static final int HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP = 19; public static final int HIERARCHY_OP_TYPE_REMOVE_TASK = 20; + public static final int HIERARCHY_OP_TYPE_FINISH_ACTIVITY = 21; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. @@ -1484,6 +1502,8 @@ public final class WindowContainerTransaction implements Parcelable { + " alwaysOnTop=" + mAlwaysOnTop + "}"; case HIERARCHY_OP_TYPE_REMOVE_TASK: return "{RemoveTask: task=" + mContainer + "}"; + case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: + return "{finishActivity: activity=" + mContainer + "}"; default: return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent + " mToTop=" + mToTop diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 8f0fc2db5d26..620f1776af33 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -1069,7 +1069,12 @@ public class ChooserActivity extends ResolverActivity implements } } - private ViewGroup createContentPreviewView(ViewGroup parent) { + /** + * Create a view that will be shown in the content preview area + * @param parent reference to the parent container where the view should be attached to + * @return content preview view + */ + protected ViewGroup createContentPreviewView(ViewGroup parent) { Intent targetIntent = getTargetIntent(); int previewType = findPreferredContentPreview(targetIntent, getContentResolver()); return displayContentPreview(previewType, targetIntent, getLayoutInflater(), parent); @@ -2640,7 +2645,7 @@ public class ChooserActivity extends ResolverActivity implements boolean isExpandable = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT && !isInMultiWindowMode(); - if (directShareHeight != 0 && isSendAction(getTargetIntent()) + if (directShareHeight != 0 && shouldShowContentPreview() && isExpandable) { // make sure to leave room for direct share 4->8 expansion int requiredExpansionHeight = @@ -2888,7 +2893,14 @@ public class ChooserActivity extends ResolverActivity implements return shouldShowTabs() && mMultiProfilePagerAdapter.getListAdapterForUserHandle( UserHandle.of(UserHandle.myUserId())).getCount() > 0 - && isSendAction(getTargetIntent()); + && shouldShowContentPreview(); + } + + /** + * @return true if we want to show the content preview area + */ + protected boolean shouldShowContentPreview() { + return isSendAction(getTargetIntent()); } private void updateStickyContentPreview() { @@ -3221,7 +3233,7 @@ public class ChooserActivity extends ResolverActivity implements return 0; } - if (!isSendAction(getTargetIntent())) { + if (!shouldShowContentPreview()) { return 0; } @@ -3252,7 +3264,7 @@ public class ChooserActivity extends ResolverActivity implements // There can be at most one row in the listview, that is internally // a ViewGroup with 2 rows public int getServiceTargetRowCount() { - if (isSendAction(getTargetIntent()) + if (shouldShowContentPreview() && !ActivityManager.isLowRamDeviceStatic()) { return 1; } diff --git a/core/java/com/android/internal/app/ILogAccessDialogCallback.aidl b/core/java/com/android/internal/app/ILogAccessDialogCallback.aidl new file mode 100644 index 000000000000..b2236c9f4907 --- /dev/null +++ b/core/java/com/android/internal/app/ILogAccessDialogCallback.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 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.internal.app; + +/** + * IPC interface for an application to receive callbacks from the log access dialog callback. + */ +oneway interface ILogAccessDialogCallback { + void approveAccessForClient(int uid, String packageName); + void declineAccessForClient(int uid, String packageName); +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/core/java/com/android/internal/app/LogAccessDialogActivity.java index 811e96ce6d82..4adb8673084b 100644 --- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java +++ b/core/java/com/android/internal/app/LogAccessDialogActivity.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.logcat; +package com.android.internal.app; import android.annotation.StyleRes; import android.app.Activity; @@ -27,7 +27,14 @@ import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.RemoteException; import android.os.UserHandle; +import android.text.Html; +import android.text.Spannable; +import android.text.TextUtils; +import android.text.method.LinkMovementMethod; +import android.text.style.TypefaceSpan; +import android.text.style.URLSpan; import android.util.Slog; import android.view.ContextThemeWrapper; import android.view.InflateException; @@ -37,7 +44,6 @@ import android.widget.Button; import android.widget.TextView; import com.android.internal.R; -import com.android.server.LocalServices; /** * Dialog responsible for obtaining user consent per-use log access @@ -45,17 +51,19 @@ import com.android.server.LocalServices; public class LogAccessDialogActivity extends Activity implements View.OnClickListener { private static final String TAG = LogAccessDialogActivity.class.getSimpleName(); + public static final String EXTRA_CALLBACK = "EXTRA_CALLBACK"; + private static final int DIALOG_TIME_OUT = Build.IS_DEBUGGABLE ? 60000 : 300000; private static final int MSG_DISMISS_DIALOG = 0; - private final LogcatManagerService.LogcatManagerServiceInternal mLogcatManagerInternal = - LocalServices.getService(LogcatManagerService.LogcatManagerServiceInternal.class); - private String mPackageName; private int mUid; + private ILogAccessDialogCallback mCallback; private String mAlertTitle; + private String mAlertBody; + private String mAlertLearnMore; private AlertDialog.Builder mAlertDialog; private AlertDialog mAlert; private View mAlertView; @@ -81,6 +89,9 @@ public class LogAccessDialogActivity extends Activity implements return; } + mAlertBody = getResources().getString(R.string.log_access_confirmation_body); + mAlertLearnMore = getResources().getString(R.string.log_access_confirmation_learn_more); + // create View boolean isDarkTheme = (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; @@ -118,6 +129,13 @@ public class LogAccessDialogActivity extends Activity implements return false; } + mCallback = ILogAccessDialogCallback.Stub.asInterface( + intent.getExtras().getBinder(EXTRA_CALLBACK)); + if (mCallback == null) { + Slog.e(TAG, "Missing callback"); + return false; + } + mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); if (mPackageName == null || mPackageName.length() == 0) { Slog.e(TAG, "Missing package name extra"); @@ -165,13 +183,22 @@ public class LogAccessDialogActivity extends Activity implements return titleString; } + private Spannable styleFont(String text) { + Spannable s = (Spannable) Html.fromHtml(text); + for (URLSpan span : s.getSpans(0, s.length(), URLSpan.class)) { + TypefaceSpan typefaceSpan = new TypefaceSpan("google-sans"); + s.setSpan(typefaceSpan, s.getSpanStart(span), s.getSpanEnd(span), 0); + } + return s; + } + /** * Returns the dialog view. * If we cannot retrieve the package name, it returns null and we decline the full device log * access */ private View createView(@StyleRes int themeId) { - Context themedContext = new ContextThemeWrapper(getApplicationContext(), themeId); + Context themedContext = new ContextThemeWrapper(this, themeId); final View view = LayoutInflater.from(themedContext).inflate( R.layout.log_access_user_consent_dialog_permission, null /*root*/); @@ -182,6 +209,19 @@ public class LogAccessDialogActivity extends Activity implements ((TextView) view.findViewById(R.id.log_access_dialog_title)) .setText(mAlertTitle); + if (!TextUtils.isEmpty(mAlertLearnMore)) { + Spannable mSpannableLearnMore = styleFont(mAlertLearnMore); + + ((TextView) view.findViewById(R.id.log_access_dialog_body)) + .setText(TextUtils.concat(mAlertBody, "\n\n", mSpannableLearnMore)); + + ((TextView) view.findViewById(R.id.log_access_dialog_body)) + .setMovementMethod(LinkMovementMethod.getInstance()); + } else { + ((TextView) view.findViewById(R.id.log_access_dialog_body)) + .setText(mAlertBody); + } + Button button_allow = (Button) view.findViewById(R.id.log_access_dialog_allow_button); button_allow.setOnClickListener(this); @@ -194,19 +234,27 @@ public class LogAccessDialogActivity extends Activity implements @Override public void onClick(View view) { - switch (view.getId()) { - case R.id.log_access_dialog_allow_button: - mLogcatManagerInternal.approveAccessForClient(mUid, mPackageName); - finish(); - break; - case R.id.log_access_dialog_deny_button: - declineLogAccess(); - finish(); - break; + try { + switch (view.getId()) { + case R.id.log_access_dialog_allow_button: + mCallback.approveAccessForClient(mUid, mPackageName); + finish(); + break; + case R.id.log_access_dialog_deny_button: + declineLogAccess(); + finish(); + break; + } + } catch (RemoteException e) { + finish(); } } private void declineLogAccess() { - mLogcatManagerInternal.declineAccessForClient(mUid, mPackageName); + try { + mCallback.declineAccessForClient(mUid, mPackageName); + } catch (RemoteException e) { + finish(); + } } } diff --git a/core/java/com/android/internal/app/SimpleIconFactory.java b/core/java/com/android/internal/app/SimpleIconFactory.java index 354eb62ba045..be0b729aedfe 100644 --- a/core/java/com/android/internal/app/SimpleIconFactory.java +++ b/core/java/com/android/internal/app/SimpleIconFactory.java @@ -51,6 +51,7 @@ import android.util.Pools.SynchronizedPool; import android.util.TypedValue; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import org.xmlpull.v1.XmlPullParser; @@ -69,6 +70,7 @@ public class SimpleIconFactory { private static final SynchronizedPool<SimpleIconFactory> sPool = new SynchronizedPool<>(Runtime.getRuntime().availableProcessors()); + private static boolean sPoolEnabled = true; private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE; private static final float BLUR_FACTOR = 1.5f / 48; @@ -92,7 +94,7 @@ public class SimpleIconFactory { */ @Deprecated public static SimpleIconFactory obtain(Context ctx) { - SimpleIconFactory instance = sPool.acquire(); + SimpleIconFactory instance = sPoolEnabled ? sPool.acquire() : null; if (instance == null) { final ActivityManager am = (ActivityManager) ctx.getSystemService(ACTIVITY_SERVICE); final int iconDpi = (am == null) ? 0 : am.getLauncherLargeIconDensity(); @@ -106,6 +108,17 @@ public class SimpleIconFactory { return instance; } + /** + * Enables or disables SimpleIconFactory objects pooling. It is enabled in production, you + * could use this method in tests and disable the pooling to make the icon rendering more + * deterministic because some sizing parameters will not be cached. Please ensure that you + * reset this value back after finishing the test. + */ + @VisibleForTesting + public static void setPoolEnabled(boolean poolEnabled) { + sPoolEnabled = poolEnabled; + } + private static int getAttrDimFromContext(Context ctx, @AttrRes int attrId, String errorMsg) { final Resources res = ctx.getResources(); TypedValue outVal = new TypedValue(); diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java index 87e8ac1be611..72b9cd272d02 100644 --- a/core/java/com/android/internal/app/procstats/ProcessState.java +++ b/core/java/com/android/internal/app/procstats/ProcessState.java @@ -473,7 +473,10 @@ public final class ProcessState { } } mCurCombinedState = state; - mStats.mUidStates.get(mUid).updateCombinedState(state, now); + final UidState uidState = mStats.mUidStates.get(mUid); + if (uidState != null) { + uidState.updateCombinedState(state, now); + } } } diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 72de78c148f8..5de72409133d 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -29,7 +29,9 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR; @@ -219,6 +221,8 @@ public class InteractionJankMonitor { public static final int CUJ_TASKBAR_EXPAND = 60; public static final int CUJ_TASKBAR_COLLAPSE = 61; public static final int CUJ_SHADE_CLEAR_ALL = 62; + public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION = 63; + public static final int CUJ_LOCKSCREEN_OCCLUSION = 64; private static final int NO_STATSD_LOGGING = -1; @@ -290,6 +294,8 @@ public class InteractionJankMonitor { UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION, }; private static volatile InteractionJankMonitor sInstance; @@ -372,7 +378,9 @@ public class InteractionJankMonitor { CUJ_USER_DIALOG_OPEN, CUJ_TASKBAR_EXPAND, CUJ_TASKBAR_COLLAPSE, - CUJ_SHADE_CLEAR_ALL + CUJ_SHADE_CLEAR_ALL, + CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION, + CUJ_LOCKSCREEN_OCCLUSION }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { @@ -431,6 +439,22 @@ public class InteractionJankMonitor { @VisibleForTesting public FrameTracker createFrameTracker(Configuration config, Session session) { final View view = config.mView; + + if (!config.hasValidView()) { + boolean attached = false; + boolean hasViewRoot = false; + boolean hasRenderer = false; + if (view != null) { + attached = view.isAttachedToWindow(); + hasViewRoot = view.getViewRootImpl() != null; + hasRenderer = view.getThreadedRenderer() != null; + } + Log.d(TAG, "create FrameTracker fails: view=" + view + + ", attached=" + attached + ", hasViewRoot=" + hasViewRoot + + ", hasRenderer=" + hasRenderer, new Throwable()); + return null; + } + final ThreadedRendererWrapper threadedRenderer = view == null ? null : new ThreadedRendererWrapper(view.getThreadedRenderer()); final ViewRootWrapper viewRoot = @@ -537,6 +561,7 @@ public class InteractionJankMonitor { // begin a new trace session. tracker = createFrameTracker(conf, new Session(cujType, conf.mTag)); + if (tracker == null) return false; putTracker(cujType, tracker); tracker.begin(); @@ -864,6 +889,10 @@ public class InteractionJankMonitor { return "TASKBAR_COLLAPSE"; case CUJ_SHADE_CLEAR_ALL: return "SHADE_CLEAR_ALL"; + case CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION: + return "LAUNCHER_UNLOCK_ENTRANCE_ANIMATION"; + case CUJ_LOCKSCREEN_OCCLUSION: + return "LOCKSCREEN_OCCLUSION"; } return "UNKNOWN"; } @@ -1054,9 +1083,19 @@ public class InteractionJankMonitor { msg.append("Must pass in a valid surface control if only instrument surface; "); } } else { - if (mView == null || !mView.isAttachedToWindow()) { + if (!hasValidView()) { shouldThrow = true; - msg.append("Null view or unattached view while instrumenting view; "); + boolean attached = false; + boolean hasViewRoot = false; + boolean hasRenderer = false; + if (mView != null) { + attached = mView.isAttachedToWindow(); + hasViewRoot = mView.getViewRootImpl() != null; + hasRenderer = mView.getThreadedRenderer() != null; + } + String err = "invalid view: view=" + mView + ", attached=" + attached + + ", hasViewRoot=" + hasViewRoot + ", hasRenderer=" + hasRenderer; + msg.append(err); } } if (shouldThrow) { @@ -1064,6 +1103,12 @@ public class InteractionJankMonitor { } } + boolean hasValidView() { + return mSurfaceOnly + || (mView != null && mView.isAttachedToWindow() + && mView.getViewRootImpl() != null && mView.getThreadedRenderer() != null); + } + /** * @return true if only instrumenting surface, false otherwise */ diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java index 7f36c79591b3..1c07ecd83a8a 100644 --- a/core/java/com/android/internal/protolog/ProtoLogGroup.java +++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java @@ -88,6 +88,7 @@ public enum ProtoLogGroup implements IProtoLogGroup { WM_DEBUG_WALLPAPER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM), WM_DEBUG_BACK_PREVIEW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, "CoreBackPreview"), + WM_DEBUG_DREAM(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, Consts.TAG_WM), TEST_GROUP(true, true, false, "WindowManagerProtoLogTest"); private final boolean mEnabled; diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index d3f9e0a2729e..8fcb6d5514d4 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -29,6 +29,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPOR import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR; +import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS; @@ -174,6 +175,12 @@ public class LatencyTracker { */ public static final int ACTION_FOLD_TO_AOD = 18; + /** + * Time it takes to show the {@link android.service.voice.VoiceInteractionSession} system UI + * after a {@link android.hardware.soundtrigger3.ISoundTriggerHw} voice trigger. + */ + public static final int ACTION_SHOW_VOICE_INTERACTION = 19; + private static final int[] ACTIONS_ALL = { ACTION_EXPAND_PANEL, ACTION_TOGGLE_RECENTS, @@ -194,6 +201,7 @@ public class LatencyTracker { ACTION_LOAD_SHARE_SHEET, ACTION_SHOW_SELECTION_TOOLBAR, ACTION_FOLD_TO_AOD, + ACTION_SHOW_VOICE_INTERACTION, }; /** @hide */ @@ -217,6 +225,7 @@ public class LatencyTracker { ACTION_LOAD_SHARE_SHEET, ACTION_SHOW_SELECTION_TOOLBAR, ACTION_FOLD_TO_AOD, + ACTION_SHOW_VOICE_INTERACTION, }) @Retention(RetentionPolicy.SOURCE) public @interface Action { @@ -243,6 +252,7 @@ public class LatencyTracker { UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET, UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR, UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD, + UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION, }; private static LatencyTracker sLatencyTracker; @@ -340,6 +350,8 @@ public class LatencyTracker { return "ACTION_SHOW_SELECTION_TOOLBAR"; case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD: return "ACTION_FOLD_TO_AOD"; + case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION: + return "ACTION_SHOW_VOICE_INTERACTION"; default: throw new IllegalArgumentException("Invalid action"); } diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index 9474f6fc3252..79c519645a24 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -377,6 +377,9 @@ public class ScreenshotHelper { msg.replyTo = new Messenger(h); if (mScreenshotConnection == null || mScreenshotService == null) { + if (mScreenshotConnection != null) { + resetConnection(); + } final ComponentName serviceComponent = ComponentName.unflattenFromString( mContext.getResources().getString( com.android.internal.R.string.config_screenshotServiceComponent)); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 1235b602cde9..a4da8de44880 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1522,7 +1522,8 @@ public class LockPatternUtils { STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, - STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT}) + STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT, + SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED}) @Retention(RetentionPolicy.SOURCE) public @interface StrongAuthFlags {} @@ -1575,6 +1576,12 @@ public class LockPatternUtils { public static final int STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT = 0x80; /** + * Some authentication is required because the trustagent either timed out or was disabled + * manually. + */ + public static final int SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED = 0x100; + + /** * Strong auth flags that do not prevent biometric methods from being accepted as auth. * If any other flags are set, biometric authentication is disabled. */ diff --git a/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbarPopup.java index f7af67b3b2a8..c484525dbe20 100644 --- a/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbarPopup.java +++ b/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbarPopup.java @@ -21,7 +21,6 @@ import android.content.Context; import android.graphics.Rect; import android.view.MenuItem; import android.view.View; -import android.view.selectiontoolbar.SelectionToolbarManager; import android.widget.PopupWindow; import java.util.List; @@ -93,10 +92,7 @@ public interface FloatingToolbarPopup { * enabled, otherwise returns {@link LocalFloatingToolbarPopup} implementation. */ static FloatingToolbarPopup createInstance(Context context, View parent) { - boolean enabled = SelectionToolbarManager.isRemoteSelectionToolbarEnabled(context); - return enabled - ? new RemoteFloatingToolbarPopup(context, parent) - : new LocalFloatingToolbarPopup(context, parent); + return new LocalFloatingToolbarPopup(context, parent); } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b91bd1898a4c..5ae133bbe6e6 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -6783,8 +6783,9 @@ android:exported="false"> </activity> - <activity android:name="com.android.server.logcat.LogAccessDialogActivity" + <activity android:name="com.android.internal.app.LogAccessDialogActivity" android:theme="@style/Theme.Translucent.NoTitleBar" + android:process=":ui" android:excludeFromRecents="true" android:exported="false"> </activity> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 0a52687b466b..7a9991b8e987 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Gee <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> toegang tot alle toestelloglêers?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Gee eenmalige toegang"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Moenie toelaat nie"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Toestelloglêers teken aan wat op jou toestel gebeur. Programme kan hierdie loglêers gebruik om kwessies op te spoor en reg te stel.\n\nSommige loglêers bevat dalk sensitiewe inligting en daarom moet jy toegang tot alle toestelloglêers net gee aan programme wat jy vertrou. \n\nAs jy nie vir hierdie program toegang tot alle toestelloglêers gee nie, het die program steeds toegang tot eie loglêers. Jou toestelvervaardiger het dalk steeds toegang tot sommige loglêers of inligting op jou toestel."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Toestelloglêers teken aan wat op jou toestel gebeur. Programme kan hierdie loglêers gebruik om kwessies op te spoor en reg te stel.\n\nSommige loglêers bevat dalk sensitiewe inligting en daarom moet jy toegang tot alle toestelloglêers net gee aan programme wat jy vertrou. \n\nAs jy nie vir hierdie program toegang tot alle toestelloglêers gee nie, het die program steeds toegang tot eie loglêers. Jou toestelvervaardiger het dalk steeds toegang tot sommige loglêers of inligting op jou toestel."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Moenie weer wys nie"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wil <xliff:g id="APP_2">%2$s</xliff:g>-skyfies wys"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Wysig"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 01565f2520f4..f47cf3fd77bd 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ሁሉንም የመሣሪያ ምዝግብ ማስታወሻዎች እንዲደርስ ይፈቀድለት?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"የአንድ ጊዜ መዳረሻን ፍቀድ"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"አትፍቀድ"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"የመሣሪያ ምዝግብ ማስታወሻዎች በመሣሪያዎ ላይ ምን እንደሚከሰት ይመዘግባሉ። መተግበሪያዎች ችግሮችን ለማግኘት እና ለማስተካከል እነዚህን ምዝግብ ማስታወሻዎች መጠቀም ይችላሉ።\n\nአንዳንድ ምዝግብ ማስታወሻዎች ሚስጥራዊነት ያለው መረጃ ሊይዙ ይችላሉ፣ ስለዚህ የሚያምኗቸውን መተግበሪያዎች ብቻ ሁሉንም የመሣሪያ ምዝግብ ማስታወሻዎች እንዲደርሱ ይፍቀዱላቸው። \n\nይህ መተግበሪያ ሁሉንም የመሣሪያ ምዝግብ ማስታወሻዎች እንዲደርስ ካልፈቀዱለት አሁንም የራሱን ምዝግብ ማስታወሻዎች መድረስ ይችላል። የእርስዎ መሣሪያ አምራች አሁንም አንዳንድ ምዝግብ ማስታወሻዎችን ወይም መረጃዎችን በመሣሪያዎ ላይ ሊደርስ ይችላል።"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"የመሣሪያ ምዝግብ ማስታወሻዎች በመሣሪያዎ ላይ ምን እንደሚከሰት ይመዘግባሉ። መተግበሪያዎች ችግሮችን ለማግኘት እና ለማስተካከል እነዚህን ምዝግብ ማስታወሻዎች መጠቀም ይችላሉ።\n\nአንዳንድ ምዝግብ ማስታወሻዎች ሚስጥራዊነት ያለው መረጃ ሊይዙ ይችላሉ፣ ስለዚህ የሚያምኗቸውን መተግበሪያዎች ብቻ ሁሉንም የመሣሪያ ምዝግብ ማስታወሻዎች እንዲደርሱ ይፍቀዱላቸው። \n\nይህ መተግበሪያ ሁሉንም የመሣሪያ ምዝግብ ማስታወሻዎች እንዲደርስ ካልፈቀዱለት አሁንም የራሱን ምዝግብ ማስታወሻዎች መድረስ ይችላል። የእርስዎ መሣሪያ አምራች አሁንም አንዳንድ ምዝግብ ማስታወሻዎችን ወይም መረጃዎችን በመሣሪያዎ ላይ ሊደርስ ይችላል።"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"ዳግም አታሳይ"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> የ<xliff:g id="APP_2">%2$s</xliff:g> ቁራጮችን ማሳየት ይፈልጋል"</string> <string name="screenshot_edit" msgid="7408934887203689207">"አርትዕ"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 9a7a1984c82b..8d47c17704bc 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -590,16 +590,14 @@ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"استخدام قفل الشاشة"</string> <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"أدخِل قفل الشاشة للمتابعة"</string> <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"اضغط بقوة على المستشعر"</string> - <!-- no translation found for fingerprint_acquired_insufficient (623888149088216458) --> - <skip /> + <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"يتعذّر التعرّف على بصمة الإصبع. يُرجى إعادة المحاولة."</string> <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"يُرجى تنظيف مستشعر بصمات الإصبع ثم إعادة المحاولة."</string> <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"تنظيف المستشعر ثم إعادة المحاولة"</string> <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"اضغط بقوة على المستشعر"</string> <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"تم تحريك الإصبع ببطء شديد. يُرجى إعادة المحاولة."</string> <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"يمكنك تجربة بصمة إصبع أخرى."</string> <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"الصورة ساطعة للغاية."</string> - <!-- no translation found for fingerprint_acquired_power_press (3107864151278434961) --> - <skip /> + <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"تم رصد الضغط على زر التشغيل."</string> <string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"حاوِل تعديل بصمة الإصبع."</string> <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"غيِّر موضع إصبعك قليلاً في كل مرة."</string> <string-array name="fingerprint_acquired_vendor"> @@ -611,15 +609,12 @@ <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"تمّت مصادقة الوجه، يُرجى الضغط على \"تأكيد\"."</string> <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"جهاز بصمة الإصبع غير متاح."</string> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"يتعذّر إعداد بصمة الإصبع."</string> - <!-- no translation found for fingerprint_error_timeout (7361192266621252164) --> - <skip /> + <string name="fingerprint_error_timeout" msgid="7361192266621252164">"انتهت مهلة إعداد بصمة الإصبع. يُرجى إعادة المحاولة."</string> <string name="fingerprint_error_canceled" msgid="540026881380070750">"تم إلغاء تشغيل بصمة الإصبع."</string> <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"تم إلغاء تشغيل بصمة الإصبع بواسطة المستخدم."</string> - <!-- no translation found for fingerprint_error_lockout (6626753679019351368) --> - <skip /> + <string name="fingerprint_error_lockout" msgid="6626753679019351368">"تم إجراء عدد كبير جدًا من المحاولات. عليك استخدام قفل الشاشة بدلاً من ذلك."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"تم إجراء عدد كبير جدًا من المحاولات. عليك استخدام قفل الشاشة بدلاً من ذلك."</string> - <!-- no translation found for fingerprint_error_unable_to_process (2446280592818621224) --> - <skip /> + <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"تتعذّر معالجة بصمة الإصبع. يُرجى إعادة المحاولة."</string> <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ليست هناك بصمات إصبع مسجَّلة."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"لا يحتوي هذا الجهاز على مستشعِر بصمات إصبع."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"تم إيقاف جهاز الاستشعار مؤقتًا."</string> @@ -1260,10 +1255,8 @@ <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"بدء التطبيقات."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"جارٍ إعادة التشغيل."</string> <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ضغطت على زر التشغيل، يؤدي هذا عادةً إلى إيقاف الشاشة.\n\nجرِّب النقر بخفة أثناء إعداد بصمتك."</string> - <!-- no translation found for fp_power_button_enrollment_title (6976841690455338563) --> - <skip /> - <!-- no translation found for fp_power_button_enrollment_button_text (3199783266386029200) --> - <skip /> + <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"لإنهاء عملية الإعداد، أوقِف الشاشة."</string> + <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"إيقاف"</string> <string name="fp_power_button_bp_title" msgid="5585506104526820067">"هل تريد مواصلة تأكيد بصمة إصبعك؟"</string> <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ضغطت على زر التشغيل، يؤدي هذا عادةً إلى إيقاف الشاشة.\n\nجرِّب النقر بخفة لتأكيد بصمة إصبعك."</string> <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"إيقاف الشاشة"</string> @@ -2061,7 +2054,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"هل تريد السماح لتطبيق <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> بالوصول إلى جميع سجلّات الجهاز؟"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"السماح بالوصول إلى السجلّ لمرة واحدة"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"عدم السماح"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"ترصد سجلّات الجهاز ما يحدث على جهازك. يمكن أن تستخدم التطبيقات هذه السجلّات لتحديد المشاكل وحلها.\n\nقد تحتوي بعض السجلّات على معلومات حساسة، ولذلك يجب عدم السماح بالوصول إلى جميع سجلّات الجهاز إلا للتطبيقات التي تثق بها. \n\nإذا لم تسمح بوصول هذا التطبيق إلى جميع سجلّات الجهاز، يظل بإمكان التطبيق الوصول إلى سجلّاته. ويظل بإمكان الشركة المصنِّعة لجهازك الوصول إلى بعض السجلّات أو المعلومات المتوفّرة على جهازك."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"ترصد سجلّات الجهاز ما يحدث على جهازك. يمكن أن تستخدم التطبيقات هذه السجلّات لتحديد المشاكل وحلها.\n\nقد تحتوي بعض السجلّات على معلومات حساسة، ولذلك يجب عدم السماح بالوصول إلى جميع سجلّات الجهاز إلا للتطبيقات التي تثق بها. \n\nإذا لم تسمح بوصول هذا التطبيق إلى جميع سجلّات الجهاز، يظل بإمكان التطبيق الوصول إلى سجلّاته. ويظل بإمكان الشركة المصنِّعة لجهازك الوصول إلى بعض السجلّات أو المعلومات المتوفّرة على جهازك."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"عدم الإظهار مرة أخرى"</string> <string name="slices_permission_request" msgid="3677129866636153406">"يريد تطبيق <xliff:g id="APP_0">%1$s</xliff:g> عرض شرائح تطبيق <xliff:g id="APP_2">%2$s</xliff:g>."</string> <string name="screenshot_edit" msgid="7408934887203689207">"تعديل"</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 6dae3ca95f76..f5fbba9a7b7a 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>ক আটাইবোৰ ডিভাইচৰ লগ এক্সেছ কৰাৰ অনুমতি প্ৰদান কৰিবনে?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"কেৱল এবাৰ এক্সেছ কৰাৰ অনুমতি দিয়ক"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"অনুমতি নিদিব"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"আপোনাৰ ডিভাইচত কি কি ঘটে সেয়া ডিভাইচ লগে ৰেকৰ্ড কৰে। এপ্সমূহে সমস্যা বিচাৰিবলৈ আৰু সমাধান কৰিবলৈ এই লগসমূহ ব্যৱহাৰ কৰিব পাৰে।\n\nকিছুমান লগত সংবেদনশীল তথ্য থাকিব পাৰে, গতিকে কেৱল আপুনি বিশ্বাস কৰা এপকহে আটাইবোৰ ডিভাইচ লগ এক্সেছ কৰাৰ অনুমতি দিয়ক। \n\nআপুনি যদি এই এপ্টোক আটাইবোৰ ডিভাইচ লগ এক্সেছ কৰাৰ অনুমতি নিদিয়ে, তথাপিও ই নিজৰ লগসমূহ এক্সেছ কৰিব পাৰিব। আপোনাৰ ডিভাইচৰ নিৰ্মাতাই তথাপিও হয়তো আপোনাৰ ডিভাইচটোত থকা কিছু লগ অথবা তথ্য এক্সেছ কৰিব পাৰিব।"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"আপোনাৰ ডিভাইচত কি কি ঘটে সেয়া ডিভাইচ লগে ৰেকৰ্ড কৰে। এপ্সমূহে সমস্যা বিচাৰিবলৈ আৰু সমাধান কৰিবলৈ এই লগসমূহ ব্যৱহাৰ কৰিব পাৰে।\n\nকিছুমান লগত সংবেদনশীল তথ্য থাকিব পাৰে, গতিকে কেৱল আপুনি বিশ্বাস কৰা এপকহে আটাইবোৰ ডিভাইচ লগ এক্সেছ কৰাৰ অনুমতি দিয়ক। \n\nআপুনি যদি এই এপ্টোক আটাইবোৰ ডিভাইচ লগ এক্সেছ কৰাৰ অনুমতি নিদিয়ে, তথাপিও ই নিজৰ লগসমূহ এক্সেছ কৰিব পাৰিব। আপোনাৰ ডিভাইচৰ নিৰ্মাতাই তথাপিও হয়তো আপোনাৰ ডিভাইচটোত থকা কিছু লগ অথবা তথ্য এক্সেছ কৰিব পাৰিব।"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"পুনৰ নেদেখুৱাব"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>এ <xliff:g id="APP_2">%2$s</xliff:g>ৰ অংশ দেখুওৱাব খুজিছে"</string> <string name="screenshot_edit" msgid="7408934887203689207">"সম্পাদনা কৰক"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 2e93dcb5b8aa..d62800f3c6f4 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> tətbiqinin bütün cihaz qeydlərinə girişinə icazə verilsin?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Birdəfəlik girişə icazə verin"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"İcazə verməyin"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Cihaz qeydləri cihazınızda baş verənləri qeyd edir. Tətbiqlər problemləri tapmaq və həll etmək üçün bu qeydlərdən istifadə edə bilər.\n\nBəzi qeydlərdə həssas məlumatlar ola bilər, ona görə də yalnız etibar etdiyiniz tətbiqlərin bütün cihaz qeydlərinə giriş etməsinə icazə verin. \n\nBu tətbiqin bütün cihaz qeydlərinə girişinə icazə verməsəniz, o, hələ də öz qeydlərinə giriş edə bilər. Cihaz istehsalçınız hələ də cihazınızda bəzi qeydlərə və ya məlumatlara giriş edə bilər."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Cihaz qeydləri cihazınızda baş verənləri qeyd edir. Tətbiqlər problemləri tapmaq və həll etmək üçün bu qeydlərdən istifadə edə bilər.\n\nBəzi qeydlərdə həssas məlumatlar ola bilər, ona görə də yalnız etibar etdiyiniz tətbiqlərin bütün cihaz qeydlərinə giriş etməsinə icazə verin. \n\nBu tətbiqin bütün cihaz qeydlərinə girişinə icazə verməsəniz, o, hələ də öz qeydlərinə giriş edə bilər. Cihaz istehsalçınız hələ də cihazınızda bəzi qeydlərə və ya məlumatlara giriş edə bilər."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Daha göstərməyin"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> <xliff:g id="APP_2">%2$s</xliff:g> tətbiqindən bölmələr göstərmək istəyir"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Redaktə edin"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index afd6cd0418b1..0d6812d85f6d 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -2051,7 +2051,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Želite da dozvolite aplikaciji <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> da pristupa svim evidencijama uređaja?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Dozvoli jednokratan pristup"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ne dozvoli"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Evidencije uređaja registruju šta se dešava na uređaju. Aplikacije mogu da koriste te evidencije da bi pronašle i rešile probleme.\n\nNeke evidencije mogu da sadrže osetljive informacije, pa pristup svim evidencijama uređaja treba da dozvoljavate samo aplikacijama u koje imate poverenja. \n\nAko ne dozvolite ovoj aplikaciji da pristupa svim evidencijama uređaja, ona i dalje može da pristupa sopstvenim evidencijama. Proizvođač uređaja će možda i dalje moći da pristupa nekim evidencijama ili informacijama na uređaju."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Evidencije uređaja registruju šta se dešava na uređaju. Aplikacije mogu da koriste te evidencije da bi pronašle i rešile probleme.\n\nNeke evidencije mogu da sadrže osetljive informacije, pa pristup svim evidencijama uređaja treba da dozvoljavate samo aplikacijama u koje imate poverenja. \n\nAko ne dozvolite ovoj aplikaciji da pristupa svim evidencijama uređaja, ona i dalje može da pristupa sopstvenim evidencijama. Proizvođač uređaja će možda i dalje moći da pristupa nekim evidencijama ili informacijama na uređaju."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne prikazuj ponovo"</string> <string name="slices_permission_request" msgid="3677129866636153406">"Aplikacija <xliff:g id="APP_0">%1$s</xliff:g> želi da prikazuje isečke iz aplikacije <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Izmeni"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 5a0021c6fc2d..a3e3db91d1e1 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -2052,7 +2052,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Дазволіць праграме \"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>\" мець доступ да ўсіх журналаў прылады?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Дазволіць аднаразовы доступ"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Не дазваляць"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Журналы прылад запісваюць усё, што адбываецца на вашай прыладзе. Праграмы выкарыстоўваюць гэтыя журналы для пошуку і выпраўлення памылак.\n\nУ некаторых журналах можа ўтрымлівацца канфідэнцыяльная інфармацыя, таму давайце доступ да ўсіх журналаў прылады толькі тым праграмам, якім вы давяраеце. \n\nКалі вы не дасце гэтай праграме доступу да ўсіх журналаў прылад, у яе ўсё роўна застанецца доступ да ўласных журналаў. Для вытворцы вашай прылады будуць даступнымі некаторыя журналы і інфармацыя на вашай прыладзе."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Журналы прылад запісваюць усё, што адбываецца на вашай прыладзе. Праграмы выкарыстоўваюць гэтыя журналы для пошуку і выпраўлення памылак.\n\nУ некаторых журналах можа ўтрымлівацца канфідэнцыяльная інфармацыя, таму давайце доступ да ўсіх журналаў прылады толькі тым праграмам, якім вы давяраеце. \n\nКалі вы не дасце гэтай праграме доступу да ўсіх журналаў прылад, у яе ўсё роўна застанецца доступ да ўласных журналаў. Для вытворцы вашай прылады будуць даступнымі некаторыя журналы і інфармацыя на вашай прыладзе."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Больш не паказваць"</string> <string name="slices_permission_request" msgid="3677129866636153406">"Праграма <xliff:g id="APP_0">%1$s</xliff:g> запытвае дазвол на паказ зрэзаў праграмы <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Рэдагаваць"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index a56f24cbf79e..7d21ba3eb29a 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Да се разреши ли на <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> достъп до всички регистрационни файлове за устройството?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Разрешаване на еднократен достъп"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Забраняване"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"В регистрационните файлове за устройството се записва какво се извършва на него. Приложенията могат да използват тези регистрационни файлове, за да откриват и отстраняват проблеми.\n\nНякои регистрационни файлове за устройството може да съдържат поверителна информация, затова разрешавайте достъп до всички тях само на приложения, на които имате доверие. \n\nАко не разрешите на това приложение достъп до всички регистрационни файлове за устройството, то пак може да осъществява достъп до собствените си регистрационни файлове. Производителят на устройството пак може да има достъп до някои регистрационни файлове или информация на устройството ви."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"В регистрационните файлове за устройството се записва какво се извършва на него. Приложенията могат да използват тези регистрационни файлове, за да откриват и отстраняват проблеми.\n\nНякои регистрационни файлове за устройството може да съдържат поверителна информация, затова разрешавайте достъп до всички тях само на приложения, на които имате доверие. \n\nАко не разрешите на това приложение достъп до всички регистрационни файлове за устройството, то пак може да осъществява достъп до собствените си регистрационни файлове. Производителят на устройството пак може да има достъп до някои регистрационни файлове или информация на устройството ви."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Да не се показва пак"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> иска да показва части от <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Редактиране"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 30c1b95aa2ca..affa2b774675 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> অ্যাপকে ডিভাইসের সব লগ অ্যাক্সেসের অনুমতি দিতে চান?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"এককালীন অ্যাক্সেসের অনুমতি দিন"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"অনুমতি দেবেন না"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"ডিভাইস লগে আপনার ডিভাইসে করা অ্যাক্টিভিটি রেকর্ড করা হয়। অ্যাপ সমস্যা খুঁজে তা সমাধান করতে এইসব লগ ব্যবহার করতে পারে।\n\nকিছু লগে সংবেদনশীল তথ্য থাকতে পারে, তাই বিশ্বাস করেন শুধুমাত্র এমন অ্যাপকেই সব ডিভাইসের লগ অ্যাক্সেসের অনুমতি দিন। \n\nআপনি এই অ্যাপকে ডিভাইসের সব লগ অ্যাক্সেস করার অনুমতি না দিলেও, এটি নিজের লগ অ্যাক্সেস করতে পারবে। ডিভাইস প্রস্তুতকারকও আপনার ডিভাইসের কিছু লগ বা তথ্য হয়ত অ্যাক্সেস করতে পারবে।"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"ডিভাইস লগে আপনার ডিভাইসে করা অ্যাক্টিভিটি রেকর্ড করা হয়। অ্যাপ সমস্যা খুঁজে তা সমাধান করতে এইসব লগ ব্যবহার করতে পারে।\n\nকিছু লগে সংবেদনশীল তথ্য থাকতে পারে, তাই বিশ্বাস করেন শুধুমাত্র এমন অ্যাপকেই সব ডিভাইসের লগ অ্যাক্সেসের অনুমতি দিন। \n\nআপনি এই অ্যাপকে ডিভাইসের সব লগ অ্যাক্সেস করার অনুমতি না দিলেও, এটি নিজের লগ অ্যাক্সেস করতে পারবে। ডিভাইস প্রস্তুতকারকও আপনার ডিভাইসের কিছু লগ বা তথ্য হয়ত অ্যাক্সেস করতে পারবে।"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"আর দেখতে চাই না"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> অ্যাপটি <xliff:g id="APP_2">%2$s</xliff:g> এর অংশ দেখাতে চায়"</string> <string name="screenshot_edit" msgid="7408934887203689207">"এডিট করুন"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index cceba58eeeaa..f59c5431ecbe 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -2051,7 +2051,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Dozvoliti aplikaciji <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> da pristupa svim zapisnicima uređaja?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Dozvoli jednokratan pristup"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nemoj dozvoliti"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Zapisnici uređaja bilježe šta se dešava na uređaju. Aplikacije mogu koristiti te zapisnike da pronađu i isprave probleme.\n\nNeki zapisnici mogu sadržavati osjetljive podatke, zato pristup svim zapisnicima uređaja dozvolite samo aplikacijama kojima vjerujete. \n\nAko ne dozvolite ovoj aplikaciji da pristupa svim zapisnicima uređaja, ona i dalje može pristupati svojim zapisnicima. Proizvođač uređaja će možda i dalje biti u stanju pristupiti nekim zapisnicima ili podacima na uređaju."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Zapisnici uređaja bilježe šta se dešava na uređaju. Aplikacije mogu koristiti te zapisnike da pronađu i isprave probleme.\n\nNeki zapisnici mogu sadržavati osjetljive podatke, zato pristup svim zapisnicima uređaja dozvolite samo aplikacijama kojima vjerujete. \n\nAko ne dozvolite ovoj aplikaciji da pristupa svim zapisnicima uređaja, ona i dalje može pristupati svojim zapisnicima. Proizvođač uređaja će možda i dalje biti u stanju pristupiti nekim zapisnicima ili podacima na uređaju."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne prikazuj ponovo"</string> <string name="slices_permission_request" msgid="3677129866636153406">"Aplikacija <xliff:g id="APP_0">%1$s</xliff:g> želi prikazati isječke aplikacije <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Uredi"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 6ce252711e1d..a5ed4e9674ad 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -1859,8 +1859,8 @@ <string name="confirm_battery_saver" msgid="5247976246208245754">"D\'acord"</string> <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals, determinades funcions i algunes connexions a la xarxa."</string> <string name="battery_saver_description" msgid="8518809702138617167">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals, determinades funcions i algunes connexions a la xarxa."</string> - <string name="data_saver_description" msgid="4995164271550590517">"Per reduir l\'ús de dades, la funció Economitzador de dades evita que determinades aplicacions enviïn o rebin dades en segon pla. L\'aplicació que estiguis fent servir podrà accedir a les dades, però menys sovint. Això vol dir, per exemple, que les imatges no es mostraran fins que no les toquis."</string> - <string name="data_saver_enable_title" msgid="7080620065745260137">"Vols activar l\'Economitzador de dades?"</string> + <string name="data_saver_description" msgid="4995164271550590517">"Per reduir l\'ús de dades, la funció Estalvi de dades evita que determinades aplicacions enviïn o rebin dades en segon pla. L\'aplicació que estiguis fent servir podrà accedir a les dades, però menys sovint. Això vol dir, per exemple, que les imatges no es mostraran fins que no les toquis."</string> + <string name="data_saver_enable_title" msgid="7080620065745260137">"Vols activar l\'Estalvi de dades?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activa"</string> <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Durant 1 minut (fins a les {formattedTime})}other{Durant # minuts (fins a les {formattedTime})}}"</string> <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Durant 1 min (fins a les {formattedTime})}other{Durant # min (fins a les {formattedTime})}}"</string> @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Vols permetre que <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> accedeixi a tots els registres del dispositiu?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permet l\'accés únic"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"No permetis"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Els registres del dispositiu inclouen informació sobre tot allò que passa al teu dispositiu. Les aplicacions poden utilitzar aquests registres per detectar i corregir problemes.\n\nÉs possible que alguns registres continguin informació sensible; per això només has de donar-hi accés a les aplicacions de confiança. \n\nEncara que no permetis que aquesta aplicació pugui accedir a tots els registres del dispositiu, podrà accedir als seus propis registres. És possible que el fabricant del dispositiu també tingui accés a alguns registres o a informació del teu dispositiu."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Els registres del dispositiu inclouen informació sobre tot allò que passa al teu dispositiu. Les aplicacions poden utilitzar aquests registres per detectar i corregir problemes.\n\nÉs possible que alguns registres continguin informació sensible; per això només has de donar-hi accés a les aplicacions de confiança. \n\nEncara que no permetis que aquesta aplicació pugui accedir a tots els registres del dispositiu, podrà accedir als seus propis registres. És possible que el fabricant del dispositiu també tingui accés a alguns registres o a informació del teu dispositiu."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"No tornis a mostrar"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> vol mostrar porcions de l\'aplicació <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Edita"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 721f59659f19..21b147d81433 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -2052,7 +2052,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Povolit aplikaci <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> přístup ke všem protokolům zařízení?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Povolit jednorázový přístup"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nepovolovat"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Do protokolů zařízení se zaznamenává, co se na zařízení děje. Aplikace tyto protokoly mohou používat k vyhledání a odstranění problémů.\n\nNěkteré protokoly mohou zahrnovat citlivé údaje. Přístup k protokolům zařízení proto povolte pouze aplikacím, kterým důvěřujete. \n\nPokud této aplikaci nepovolíte přístup ke všem protokolům zařízení, bude mít stále přístup ke svým vlastním protokolům. Výrobce zařízení může mít stále přístup k některým protokolům nebo informacím na vašem zařízení."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Do protokolů zařízení se zaznamenává, co se na zařízení děje. Aplikace tyto protokoly mohou používat k vyhledání a odstranění problémů.\n\nNěkteré protokoly mohou zahrnovat citlivé údaje. Přístup k protokolům zařízení proto povolte pouze aplikacím, kterým důvěřujete. \n\nPokud této aplikaci nepovolíte přístup ke všem protokolům zařízení, bude mít stále přístup ke svým vlastním protokolům. Výrobce zařízení může mít stále přístup k některým protokolům nebo informacím na vašem zařízení."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Příště nezobrazovat"</string> <string name="slices_permission_request" msgid="3677129866636153406">"Aplikace <xliff:g id="APP_0">%1$s</xliff:g> chce zobrazovat ukázky z aplikace <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Upravit"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 53297dc6f7fa..80af569baea9 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Vil du give <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> adgang til alle enhedslogs?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Tillad engangsadgang"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Tillad ikke"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Enhedslogs registrerer, hvad der sker på din enhed. Apps kan bruge disse logs til at finde og løse problemer.\n\nNogle logs kan indeholde følsomme oplysninger, så giv kun apps, du har tillid til, adgang til alle enhedslogs. \n\nSelvom du ikke giver denne app adgang til alle enhedslogs, kan den stadig tilgå sine egne logs. Producenten af din enhed kan muligvis fortsat tilgå visse logs eller oplysninger på din enhed."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Enhedslogs registrerer, hvad der sker på din enhed. Apps kan bruge disse logs til at finde og løse problemer.\n\nNogle logs kan indeholde følsomme oplysninger, så giv kun apps, du har tillid til, adgang til alle enhedslogs. \n\nSelvom du ikke giver denne app adgang til alle enhedslogs, kan den stadig tilgå sine egne logs. Producenten af din enhed kan muligvis fortsat tilgå visse logs eller oplysninger på din enhed."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Vis ikke igen"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> anmoder om tilladelse til at vise eksempler fra <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Rediger"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 0bdfd8f52d8b..dc9d746459cd 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> den Zugriff auf alle Geräteprotokolle erlauben?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Einmaligen Zugriff zulassen"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nicht zulassen"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"In Geräteprotokollen wird aufgezeichnet, welche Aktionen auf deinem Gerät ausgeführt werden. Apps können diese Protokolle verwenden, um Probleme zu finden und zu beheben.\n\nEinige Protokolle enthalten unter Umständen vertrauliche Informationen, daher solltest du nur vertrauenswürdigen Apps den Zugriff auf alle Geräteprotokolle erlauben. \n\nWenn du dieser App keinen Zugriff auf alle Geräteprotokolle gewährst, kann sie trotzdem auf ihre eigenen Protokolle zugreifen. Dein Gerätehersteller hat möglicherweise auch Zugriff auf einige Protokolle oder Informationen auf deinem Gerät."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"In Geräteprotokollen wird aufgezeichnet, welche Aktionen auf deinem Gerät ausgeführt werden. Apps können diese Protokolle verwenden, um Probleme zu finden und zu beheben.\n\nEinige Protokolle enthalten unter Umständen vertrauliche Informationen, daher solltest du nur vertrauenswürdigen Apps den Zugriff auf alle Geräteprotokolle erlauben. \n\nWenn du dieser App keinen Zugriff auf alle Geräteprotokolle gewährst, kann sie trotzdem auf ihre eigenen Protokolle zugreifen. Dein Gerätehersteller hat möglicherweise auch Zugriff auf einige Protokolle oder Informationen auf deinem Gerät."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Nicht mehr anzeigen"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> möchte Teile von <xliff:g id="APP_2">%2$s</xliff:g> anzeigen"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Bearbeiten"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index a3e2826ff9f8..1dd2e65f5f69 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Να επιτρέπεται στο <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> η πρόσβαση σε όλα τα αρχεία καταγραφής συσκευής;"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Να επιτρέπεται η πρόσβαση για μία φορά"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Να μην επιτραπεί"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Τα αρχεία καταγραφής συσκευής καταγράφουν ό,τι συμβαίνει στη συσκευή σας. Οι εφαρμογές μπορούν να χρησιμοποιούν αυτά τα αρχεία καταγραφής για να εντοπίζουν και να διορθώνουν ζητήματα.\n\nΟρισμένα αρχεία καταγραφής ενδέχεται να περιέχουν ευαίσθητες πληροφορίες. Ως εκ τούτου, επιτρέψτε την πρόσβαση σε όλα τα αρχεία καταγραφής συσκευής μόνο στις εφαρμογές που εμπιστεύεστε. \n\nΕάν δεν επιτρέψετε σε αυτήν την εφαρμογή την πρόσβαση σε όλα τα αρχεία καταγραφής συσκευής, η εφαρμογή εξακολουθεί να έχει πρόσβαση στα δικά της αρχεία καταγραφής. Ο κατασκευαστής της συσκευής σας ενδέχεται να εξακολουθεί να έχει πρόσβαση σε ορισμένα αρχεία καταγραφής ή ορισμένες πληροφορίες στη συσκευή σας."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Τα αρχεία καταγραφής συσκευής καταγράφουν ό,τι συμβαίνει στη συσκευή σας. Οι εφαρμογές μπορούν να χρησιμοποιούν αυτά τα αρχεία καταγραφής για να εντοπίζουν και να διορθώνουν ζητήματα.\n\nΟρισμένα αρχεία καταγραφής ενδέχεται να περιέχουν ευαίσθητες πληροφορίες. Ως εκ τούτου, επιτρέψτε την πρόσβαση σε όλα τα αρχεία καταγραφής συσκευής μόνο στις εφαρμογές που εμπιστεύεστε. \n\nΕάν δεν επιτρέψετε σε αυτήν την εφαρμογή την πρόσβαση σε όλα τα αρχεία καταγραφής συσκευής, η εφαρμογή εξακολουθεί να έχει πρόσβαση στα δικά της αρχεία καταγραφής. Ο κατασκευαστής της συσκευής σας ενδέχεται να εξακολουθεί να έχει πρόσβαση σε ορισμένα αρχεία καταγραφής ή ορισμένες πληροφορίες στη συσκευή σας."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Να μην εμφανισ. ξανά"</string> <string name="slices_permission_request" msgid="3677129866636153406">"Η εφαρμογή <xliff:g id="APP_0">%1$s</xliff:g> θέλει να εμφανίζει τμήματα της εφαρμογής <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Επεξεργασία"</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 88433c6206cc..cd10c3ba755c 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Allow <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> to access all device logs?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Allow one-time access"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Don’t allow"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps that you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps that you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Don’t show again"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 24d135dc3068..ef16aefb0b08 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Allow <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> to access all device logs?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Allow one-time access"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Don’t allow"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps that you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps that you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Don’t show again"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 3e758bec3c4d..759c2f63990e 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Allow <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> to access all device logs?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Allow one-time access"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Don’t allow"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps that you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps that you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Don’t show again"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 9f251ca6a276..9d1fc96a1c95 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Allow <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> to access all device logs?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Allow one-time access"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Don’t allow"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps that you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps that you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Don’t show again"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 60675ba05356..77a1af62382a 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -2050,7 +2050,8 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Allow <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> to access all device logs?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Allow one-time access"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Don’t allow"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device."</string> + <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device.\n\nLearn more at g.co/android/devicelogs."</string> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Don’t show again"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 1d6cd7d293d6..e58e74dd73d1 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -2051,7 +2051,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"¿Quieres permitir que <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> acceda a todos los registros del dispositivo?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permitir acceso por única vez"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"No permitir"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Los registros del dispositivo permiten documentar lo que sucede en él. Las apps pueden usar estos registros para encontrar y solucionar problemas.\n\nEs posible que algunos registros del dispositivo contengan información sensible, por lo que solo debes permitir que accedan a todos ellos las apps que sean de tu confianza. \n\nSi no permites que esta app acceda a todos los registros del dispositivo, aún puede acceder a sus propios registros. Además, es posible que el fabricante del dispositivo acceda a algunos registros o información en tu dispositivo."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Los registros del dispositivo permiten documentar lo que sucede en él. Las apps pueden usar estos registros para encontrar y solucionar problemas.\n\nEs posible que algunos registros del dispositivo contengan información sensible, por lo que solo debes permitir que accedan a todos ellos las apps que sean de tu confianza. \n\nSi no permites que esta app acceda a todos los registros del dispositivo, aún puede acceder a sus propios registros. Además, es posible que el fabricante del dispositivo acceda a algunos registros o información en tu dispositivo."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"No volver a mostrar"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> quiere mostrar fragmentos de <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Editar"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 1195e54d218e..0969a5fa0fce 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -2051,7 +2051,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"¿Permitir que <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> acceda a todos los registros del dispositivo?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permitir el acceso una vez"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"No permitir"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Los registros del dispositivo documentan lo que sucede en tu dispositivo. Las aplicaciones pueden usar estos registros para encontrar y solucionar problemas.\n\nComo algunos registros pueden contener información sensible, es mejor que solo permitas que accedan a ellos las aplicaciones en las que confíes. \n\nAunque no permitas que esta aplicación acceda a todos los registros del dispositivo, aún podrá acceder a sus propios registros. El fabricante de tu dispositivo aún puede acceder a algunos registros o información de tu dispositivo."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Los registros del dispositivo documentan lo que sucede en tu dispositivo. Las aplicaciones pueden usar estos registros para encontrar y solucionar problemas.\n\nComo algunos registros pueden contener información sensible, es mejor que solo permitas que accedan a ellos las aplicaciones en las que confíes. \n\nAunque no permitas que esta aplicación acceda a todos los registros del dispositivo, aún podrá acceder a sus propios registros. El fabricante de tu dispositivo aún puede acceder a algunos registros o información de tu dispositivo."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"No volver a mostrar"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> quiere mostrar fragmentos de <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Editar"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 3f7586e6f7b9..1ed44c83f846 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Kas anda rakendusele <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> juurdepääs kõigile seadmelogidele?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Luba ühekordne juurdepääs"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ära luba"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Seadmelogid jäädvustavad, mis teie seadmes toimub. Rakendused saavad neid logisid kasutada probleemide tuvastamiseks ja lahendamiseks.\n\nMõned logid võivad sisaldada tundlikku teavet, seega lubage juurdepääs kõigile seadmelogidele ainult rakendustele, mida usaldate. \n\nKui te ei luba sellel rakendusel kõigile seadmelogidele juurde pääseda, pääseb see siiski juurde oma logidele. Teie seadme tootja võib teie seadmes siiski teatud logidele või teabele juurde pääseda."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Seadmelogid jäädvustavad, mis teie seadmes toimub. Rakendused saavad neid logisid kasutada probleemide tuvastamiseks ja lahendamiseks.\n\nMõned logid võivad sisaldada tundlikku teavet, seega lubage juurdepääs kõigile seadmelogidele ainult rakendustele, mida usaldate. \n\nKui te ei luba sellel rakendusel kõigile seadmelogidele juurde pääseda, pääseb see siiski juurde oma logidele. Teie seadme tootja võib teie seadmes siiski teatud logidele või teabele juurde pääseda."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ära kuva uuesti"</string> <string name="slices_permission_request" msgid="3677129866636153406">"Rakendus <xliff:g id="APP_0">%1$s</xliff:g> soovib näidata rakenduse <xliff:g id="APP_2">%2$s</xliff:g> lõike"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Muuda"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 29f0cab3c2b2..954c683ff3e7 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -56,7 +56,7 @@ </plurals> <string name="imei" msgid="2157082351232630390">"IMEIa"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> - <string name="ClipMmi" msgid="4110549342447630629">"Sarrerako deien identifikazio-zerbitzua"</string> + <string name="ClipMmi" msgid="4110549342447630629">"Deitzailearen identitatea (jasotako deiak)"</string> <string name="ClirMmi" msgid="6752346475055446417">"Ezkutatu irteerako deitzailearen identitatea"</string> <string name="ColpMmi" msgid="4736462893284419302">"Konektatutako linearen IDa"</string> <string name="ColrMmi" msgid="5889782479745764278">"Konektatutako linearen ID murriztapena"</string> @@ -71,12 +71,12 @@ <string name="RuacMmi" msgid="1876047385848991110">"Nahigabeko dei gogaikarriak ukatzea"</string> <string name="CndMmi" msgid="185136449405618437">"Deitzailearen zenbakia ematea"</string> <string name="DndMmi" msgid="8797375819689129800">"Ez molestatzeko modua"</string> - <string name="CLIRDefaultOnNextCallOn" msgid="4511621022859867988">"Deien identifikazio-zerbitzuaren balio lehenetsiak murriztapenak ezartzen ditu. Hurrengo deia: murriztapenekin"</string> - <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Deien identifikazio-zerbitzuaren balio lehenetsiak murriztapenak ezartzen ditu. Hurrengo deia: murriztapenik gabe"</string> - <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Deien identifikazio-zerbitzuaren balio lehenetsiak ez du murriztapenik ezartzen. Hurrengo deia: murriztapenekin"</string> - <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Deien identifikazio-zerbitzuaren balio lehenetsiak ez du murriztapenik ezartzen. Hurrengo deia: murriztapenik gabe"</string> + <string name="CLIRDefaultOnNextCallOn" msgid="4511621022859867988">"Deitzailearen identitatea zerbitzuaren balio lehenetsiak murriztapenak ezartzen ditu. Hurrengo deia: murriztapenekin"</string> + <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Deitzailearen identitatea adierazteko zerbitzuaren balio lehenetsiak murriztapenak ezartzen ditu. Hurrengo deia: murriztapenik gabe."</string> + <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Deitzailearen identitatea zerbitzuaren balio lehenetsiak ez du murriztapenik ezartzen. Hurrengo deia: murriztapenekin."</string> + <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Deitzailearen identitatea zerbitzuaren balio lehenetsiak ez du murriztapenik ezartzen. Hurrengo deia: murriztapenik gabe."</string> <string name="serviceNotProvisioned" msgid="8289333510236766193">"Zerbitzua ez da hornitu."</string> - <string name="CLIRPermanent" msgid="166443681876381118">"Ezin duzu deien identifikazio-zerbitzuaren ezarpena aldatu."</string> + <string name="CLIRPermanent" msgid="166443681876381118">"Ezin duzu aldatu deitzailearen identitatearen ezarpena."</string> <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Ez dago mugikorreko datu-zerbitzurik"</string> <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Ezin da egin larrialdi-deirik"</string> <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ez dago ahots-deien zerbitzurik"</string> @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Gailuko erregistro guztiak atzitzeko baimena eman nahi diozu <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> aplikazioari?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Eman behin erabiltzeko baimena"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ez eman baimenik"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Gailuko erregistroetan gailuan gertatzen den guztia gordetzen da. Arazoak bilatu eta konpontzeko erabil ditzakete aplikazioek erregistro horiek.\n\nBaliteke erregistro batzuek kontuzko informazioa edukitzea. Beraz, eman gailuko erregistro guztiak atzitzeko baimena fidagarritzat jotzen dituzun aplikazioei bakarrik. \n\nNahiz eta gailuko erregistro guztiak atzitzeko baimena ez eman aplikazio honi, aplikazioak hari dagozkion erregistroak atzitu ahalko ditu. Gainera, baliteke gailuaren fabrikatzaileak gailuko erregistro edo datu batzuk atzitu ahal izatea."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Gailuko erregistroetan gailuan gertatzen den guztia gordetzen da. Arazoak bilatu eta konpontzeko erabil ditzakete aplikazioek erregistro horiek.\n\nBaliteke erregistro batzuek kontuzko informazioa edukitzea. Beraz, eman gailuko erregistro guztiak atzitzeko baimena fidagarritzat jotzen dituzun aplikazioei bakarrik. \n\nNahiz eta gailuko erregistro guztiak atzitzeko baimena ez eman aplikazio honi, aplikazioak hari dagozkion erregistroak atzitu ahalko ditu. Gainera, baliteke gailuaren fabrikatzaileak gailuko erregistro edo datu batzuk atzitu ahal izatea."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ez erakutsi berriro"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> aplikazioak <xliff:g id="APP_2">%2$s</xliff:g> aplikazioaren zatiak erakutsi nahi ditu"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Editatu"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 70178a0e08c7..424efe1edf6b 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1493,8 +1493,8 @@ <string name="vpn_title_long" msgid="6834144390504619998">"VPN را <xliff:g id="APP">%s</xliff:g> فعال کرده است"</string> <string name="vpn_text" msgid="2275388920267251078">"برای مدیریت شبکه ضربه بزنید."</string> <string name="vpn_text_long" msgid="278540576806169831">"به <xliff:g id="SESSION">%s</xliff:g> متصل شد. برای مدیریت شبکه ضربه بزنید."</string> - <string name="vpn_lockdown_connecting" msgid="6096725311950342607">"در حال اتصال VPN همیشه فعال…"</string> - <string name="vpn_lockdown_connected" msgid="2853127976590658469">"VPN همیشه فعال متصل شد"</string> + <string name="vpn_lockdown_connecting" msgid="6096725311950342607">"درحال اتصال به VPN همیشه روشن…"</string> + <string name="vpn_lockdown_connected" msgid="2853127976590658469">"VPN همیشه روشن متصل شد"</string> <string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"از «VPN همیشه روشن» قطع شد"</string> <string name="vpn_lockdown_error" msgid="4453048646854247947">"به «VPN همیشه روشن» متصل نشد"</string> <string name="vpn_lockdown_config" msgid="8331697329868252169">"تغییر شبکه یا تنظیمات VPN"</string> @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"به <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> اجازه میدهید به همه گزارشهای دستگاه دسترسی داشته باشد؟"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"مجاز کردن دسترسی یکباره"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"اجازه ندادن"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"گزارشهای دستگاه آنچه را در دستگاهتان رخ میدهد ثبت میکند. برنامهها میتوانند از این گزارشها برای پیدا کردن مشکلات و رفع آنها استفاده کنند.\n\nبرخیاز گزارشها ممکن است حاوی اطلاعات حساس باشند، بنابراین فقط به برنامههای مورداعتمادتان اجازه دسترسی به همه گزارشهای دستگاه را بدهید. \n\nاگر به این برنامه اجازه ندهید به همه گزارشهای دستگاه دسترسی داشته باشد، همچنان میتواند به گزارشهای خودش دسترسی داشته باشد. سازنده دستگاه نیز ممکن است همچنان بتواند به برخیاز گزارشها یا اطلاعات دستگاهتان دسترسی داشته باشد."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"گزارشهای دستگاه آنچه را در دستگاهتان رخ میدهد ثبت میکند. برنامهها میتوانند از این گزارشها برای پیدا کردن مشکلات و رفع آنها استفاده کنند.\n\nبرخیاز گزارشها ممکن است حاوی اطلاعات حساس باشند، بنابراین فقط به برنامههای مورداعتمادتان اجازه دسترسی به همه گزارشهای دستگاه را بدهید. \n\nاگر به این برنامه اجازه ندهید به همه گزارشهای دستگاه دسترسی داشته باشد، همچنان میتواند به گزارشهای خودش دسترسی داشته باشد. سازنده دستگاه نیز ممکن است همچنان بتواند به برخیاز گزارشها یا اطلاعات دستگاهتان دسترسی داشته باشد."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"دوباره نشان داده نشود"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> میخواهد تکههای <xliff:g id="APP_2">%2$s</xliff:g> را نشان دهد"</string> <string name="screenshot_edit" msgid="7408934887203689207">"ویرایش"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 3bf91289ee7d..3f2b4ceb8d3e 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Saako <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> pääsyn kaikkiin laitelokeihin?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Salli kertaluonteinen pääsy"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Älä salli"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Laitteen tapahtumat tallentuvat laitelokeihin. Niiden avulla sovellukset voivat löytää ja korjata ongelmia.\n\nJotkin lokit voivat sisältää arkaluontoista tietoa, joten salli pääsy kaikkiin laitelokeihin vain sovelluksille, joihin luotat. \n\nJos et salli tälle sovellukselle pääsyä kaikkiin laitelokeihin, sillä on kuitenkin pääsy sen omiin lokeihin. Laitteen valmistajalla voi olla pääsy joihinkin lokeihin tai tietoihin laitteella."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Laitteen tapahtumat tallentuvat laitelokeihin. Niiden avulla sovellukset voivat löytää ja korjata ongelmia.\n\nJotkin lokit voivat sisältää arkaluontoista tietoa, joten salli pääsy kaikkiin laitelokeihin vain sovelluksille, joihin luotat. \n\nJos et salli tälle sovellukselle pääsyä kaikkiin laitelokeihin, sillä on kuitenkin pääsy sen omiin lokeihin. Laitteen valmistajalla voi olla pääsy joihinkin lokeihin tai tietoihin laitteella."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Älä näytä uudelleen"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> haluaa näyttää osia sovelluksesta <xliff:g id="APP_2">%2$s</xliff:g>."</string> <string name="screenshot_edit" msgid="7408934887203689207">"Muokkaa"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 67a5d4f11791..12fc57879d26 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -2051,7 +2051,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Autoriser <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> à accéder à l\'ensemble des journaux de l\'appareil?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Autoriser un accès unique"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ne pas autoriser"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Les journaux de l\'appareil enregistrent ce qui se passe sur celui-ci. Les applications peuvent utiliser ces journaux pour trouver et résoudre des problèmes.\n\nCertains journaux peuvent contenir des renseignements confidentiels. N\'autorisez donc que les applications auxquelles vous faites confiance puisque celles-ci pourront accéder à l\'ensemble des journaux de l\'appareil. \n\nMême si vous n\'autorisez pas cette application à accéder à l\'ensemble des journaux de l\'appareil, elle aura toujours accès à ses propres journaux. Le fabricant de votre appareil pourrait toujours être en mesure d\'accéder à certains journaux ou renseignements sur votre appareil."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Les journaux de l\'appareil enregistrent ce qui se passe sur celui-ci. Les applications peuvent utiliser ces journaux pour trouver et résoudre des problèmes.\n\nCertains journaux peuvent contenir des renseignements confidentiels. N\'autorisez donc que les applications auxquelles vous faites confiance puisque celles-ci pourront accéder à l\'ensemble des journaux de l\'appareil. \n\nMême si vous n\'autorisez pas cette application à accéder à l\'ensemble des journaux de l\'appareil, elle aura toujours accès à ses propres journaux. Le fabricant de votre appareil pourrait toujours être en mesure d\'accéder à certains journaux ou renseignements sur votre appareil."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne plus afficher"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> souhaite afficher <xliff:g id="APP_2">%2$s</xliff:g> tranches"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Modifier"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index a844870c0961..204c658c0b63 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -2051,7 +2051,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Autoriser <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> à accéder à tous les journaux de l\'appareil ?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Autoriser un accès unique"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ne pas autoriser"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Les journaux enregistrent ce qui se passe sur votre appareil. Les applis peuvent les utiliser pour rechercher et résoudre les problèmes.\n\nCertains journaux pouvant contenir des infos sensibles, autorisez uniquement les applis de confiance à accéder à tous les journaux de l\'appareil. \n\nSi vous refusez à cette appli l\'accès à tous les journaux de l\'appareil, elle a quand même accès aux siens. Le fabricant de l\'appareil peut accéder à certains journaux ou certaines infos sur votre appareil."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Les journaux enregistrent ce qui se passe sur votre appareil. Les applis peuvent les utiliser pour rechercher et résoudre les problèmes.\n\nCertains journaux pouvant contenir des infos sensibles, autorisez uniquement les applis de confiance à accéder à tous les journaux de l\'appareil. \n\nSi vous refusez à cette appli l\'accès à tous les journaux de l\'appareil, elle a quand même accès aux siens. Le fabricant de l\'appareil peut accéder à certains journaux ou certaines infos sur votre appareil."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne plus afficher"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> souhaite afficher des éléments de <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Modifier"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 0d2ea3c7c168..b092453bebcf 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -213,7 +213,7 @@ <string name="power_dialog" product="tablet" msgid="8333207765671417261">"Opcións da tableta"</string> <string name="power_dialog" product="tv" msgid="7792839006640933763">"Opcións de Android TV"</string> <string name="power_dialog" product="default" msgid="1107775420270203046">"Opcións do teléfono"</string> - <string name="silent_mode" msgid="8796112363642579333">"Modo de silencio"</string> + <string name="silent_mode" msgid="8796112363642579333">"Modo silencioso"</string> <string name="turn_on_radio" msgid="2961717788170634233">"Activar a conexión sen fíos"</string> <string name="turn_off_radio" msgid="7222573978109933360">"Desactivar a conexión sen fíos"</string> <string name="screen_lock" msgid="2072642720826409809">"Bloqueo de pantalla"</string> @@ -257,7 +257,7 @@ <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Vaise facer unha captura de pantalla para o informe de erro dentro de # segundo.}other{Vaise facer unha captura de pantalla para o informe de erro dentro de # segundos.}}"</string> <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Realizouse a captura de pantalla co informe de erros"</string> <string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"Produciuse un erro ao realizar a captura de pantalla co informe de erros"</string> - <string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Modo de silencio"</string> + <string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Modo silencioso"</string> <string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"O son está desactivado"</string> <string name="global_action_silent_mode_off_status" msgid="6608006545950920042">"O son está activado"</string> <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"Modo avión"</string> @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Queres permitir que a aplicación <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> acceda a todos os rexistros do dispositivo?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permitir acceso unha soa vez"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Non permitir"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Os rexistros do dispositivo dan conta do que ocorre neste. As aplicacións poden usalos para buscar problemas e solucionalos.\n\nAlgúns poden conter información confidencial, polo que che recomendamos que só permitas que accedan a todos os rexistros do dispositivo as aplicacións nas que confíes. \n\nEsta aplicación pode acceder aos seus propios rexistros aínda que non lle permitas acceder a todos. É posible que o fabricante do dispositivo teña acceso a algúns rexistros ou á información do teu dispositivo."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Os rexistros do dispositivo dan conta do que ocorre neste. As aplicacións poden usalos para buscar problemas e solucionalos.\n\nAlgúns poden conter información confidencial, polo que che recomendamos que só permitas que accedan a todos os rexistros do dispositivo as aplicacións nas que confíes. \n\nEsta aplicación pode acceder aos seus propios rexistros aínda que non lle permitas acceder a todos. É posible que o fabricante do dispositivo teña acceso a algúns rexistros ou á información do teu dispositivo."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Non amosar outra vez"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> quere mostrar fragmentos de aplicación de <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Editar"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 494158ca61ee..96246a650737 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>ને ડિવાઇસનો બધો લૉગ ઍક્સેસ કરવાની મંજૂરી આપવી છે?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"એક-વખતના ઍક્સેસની મંજૂરી આપો"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"મંજૂરી આપશો નહીં"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"તમારા ડિવાઇસ પર થતી કામગીરીને ડિવાઇસ લૉગ રેકોર્ડ કરે છે. ઍપ આ લૉગનો ઉપયોગ સમસ્યાઓ શોધી તેનું નિરાકરણ કરવા માટે કરી શકે છે.\n\nઅમુક લૉગમાં સંવેદનશીલ માહિતી હોઈ શકે, આથી ડિવાઇસનો બધો લૉગ ઍક્સેસ કરવાની મંજૂરી માત્ર તમારી વિશ્વાસપાત્ર ઍપને જ આપો. \n\nજો તમે આ ઍપને ડિવાઇસનો બધો લૉગ ઍક્સેસ કરવાની મંજૂરી ન આપો, તો પણ તે તેના પોતાના લૉગ ઍક્સેસ કરી શકે છે. તમારા ડિવાઇસના નિર્માતા હજુ પણ કદાચ તમારા ડિવાઇસ પર અમુક લૉગ અથવા માહિતી ઍક્સેસ કરી શકે છે."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"તમારા ડિવાઇસ પર થતી કામગીરીને ડિવાઇસ લૉગ રેકોર્ડ કરે છે. ઍપ આ લૉગનો ઉપયોગ સમસ્યાઓ શોધી તેનું નિરાકરણ કરવા માટે કરી શકે છે.\n\nઅમુક લૉગમાં સંવેદનશીલ માહિતી હોઈ શકે, આથી ડિવાઇસનો બધો લૉગ ઍક્સેસ કરવાની મંજૂરી માત્ર તમારી વિશ્વાસપાત્ર ઍપને જ આપો. \n\nજો તમે આ ઍપને ડિવાઇસનો બધો લૉગ ઍક્સેસ કરવાની મંજૂરી ન આપો, તો પણ તે તેના પોતાના લૉગ ઍક્સેસ કરી શકે છે. તમારા ડિવાઇસના નિર્માતા હજુ પણ કદાચ તમારા ડિવાઇસ પર અમુક લૉગ અથવા માહિતી ઍક્સેસ કરી શકે છે."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"ફરીથી બતાવશો નહીં"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>એ <xliff:g id="APP_2">%2$s</xliff:g> સ્લાઇસ બતાવવા માગે છે"</string> <string name="screenshot_edit" msgid="7408934887203689207">"ફેરફાર કરો"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 41436f09caff..cb0e30daba2e 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"क्या <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> को डिवाइस लॉग का ऐक्सेस देना है?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"एक बार ऐक्सेस करने की अनुमति दें"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"अनुमति न दें"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"डिवाइस लॉग में, आपके डिवाइस पर की गई कार्रवाइयां रिकॉर्ड होती हैं. ऐप्लिकेशन, इन लॉग का इस्तेमाल गड़बड़ियां ढूंढने और उन्हें ठीक करने के लिए करते हैं.\n\nकुछ लॉग में संवेदनशील जानकारी हो सकती है. इसलिए, सिर्फ़ भरोसेमंद ऐप्लिकेशन को डिवाइस लॉग का ऐक्सेस दें. \n\nअगर इस ऐप्लिकेशन को डिवाइस के सभी लॉग का ऐक्सेस नहीं दिया जाता है, तब भी यह डिवाइस पर अपने लॉग को ऐक्सेस कर सकता है. डिवाइस को बनाने वाली कंपनी अब भी डिवाइस के कुछ लॉग या जानकारी को ऐक्सेस कर सकती है."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"डिवाइस लॉग में, आपके डिवाइस पर की गई कार्रवाइयां रिकॉर्ड होती हैं. ऐप्लिकेशन, इन लॉग का इस्तेमाल गड़बड़ियां ढूंढने और उन्हें ठीक करने के लिए करते हैं.\n\nकुछ लॉग में संवेदनशील जानकारी हो सकती है. इसलिए, सिर्फ़ भरोसेमंद ऐप्लिकेशन को डिवाइस लॉग का ऐक्सेस दें. \n\nअगर इस ऐप्लिकेशन को डिवाइस के सभी लॉग का ऐक्सेस नहीं दिया जाता है, तब भी यह डिवाइस पर अपने लॉग को ऐक्सेस कर सकता है. डिवाइस को बनाने वाली कंपनी अब भी डिवाइस के कुछ लॉग या जानकारी को ऐक्सेस कर सकती है."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"फिर से न दिखाएं"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>, <xliff:g id="APP_2">%2$s</xliff:g> के हिस्से (स्लाइस) दिखाना चाहता है"</string> <string name="screenshot_edit" msgid="7408934887203689207">"बदलाव करें"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 38ca0f884212..c7fc1ef36ef0 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -2051,7 +2051,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Želite li dopustiti aplikaciji <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> da pristupa svim zapisnicima uređaja?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Omogući jednokratni pristup"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nemoj dopustiti"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"U zapisnicima uređaja bilježi se što se događa na uređaju. Aplikacije mogu koristiti te zapisnike kako bi pronašle i riješile poteškoće.\n\nNeki zapisnici mogu sadržavati osjetljive podatke, pa pristup svim zapisnicima uređaja odobrite samo pouzdanim aplikacijama. \n\nAko ne dopustite ovoj aplikaciji da pristupa svim zapisnicima uređaja, ona i dalje može pristupati svojim zapisnicima. Proizvođač vašeg uređaja i dalje može pristupati nekim zapisnicima ili podacima na vašem uređaju."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"U zapisnicima uređaja bilježi se što se događa na uređaju. Aplikacije mogu koristiti te zapisnike kako bi pronašle i riješile poteškoće.\n\nNeki zapisnici mogu sadržavati osjetljive podatke, pa pristup svim zapisnicima uređaja odobrite samo pouzdanim aplikacijama. \n\nAko ne dopustite ovoj aplikaciji da pristupa svim zapisnicima uređaja, ona i dalje može pristupati svojim zapisnicima. Proizvođač vašeg uređaja i dalje može pristupati nekim zapisnicima ili podacima na vašem uređaju."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne prikazuj ponovo"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> želi prikazivati isječke aplikacije <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Uredi"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 764a7215fdfa..b6c40ebdf9be 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Engedélyezi a(z) <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> számára, hogy hozzáférjen az összes eszköznaplóhoz?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Egyszeri hozzáférés engedélyezése"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Tiltás"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Az eszköznaplók rögzítik, hogy mi történik az eszközén. Az alkalmazások ezeket a naplókat használhatják a problémák megkeresésére és kijavítására.\n\nBizonyos naplók bizalmas adatokat is tartalmazhatnak, ezért csak olyan alkalmazások számára engedélyezze az összes eszköznaplóhoz való hozzáférést, amelyekben megbízik. \n\nHa nem engedélyezi ennek az alkalmazásnak, hogy hozzáférjen az összes eszköznaplójához, az app továbbra is hozzáférhet a saját naplóihoz. Előfordulhat, hogy az eszköz gyártója továbbra is hozzáfér az eszközön található bizonyos naplókhoz és adatokhoz."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Az eszköznaplók rögzítik, hogy mi történik az eszközén. Az alkalmazások ezeket a naplókat használhatják a problémák megkeresésére és kijavítására.\n\nBizonyos naplók bizalmas adatokat is tartalmazhatnak, ezért csak olyan alkalmazások számára engedélyezze az összes eszköznaplóhoz való hozzáférést, amelyekben megbízik. \n\nHa nem engedélyezi ennek az alkalmazásnak, hogy hozzáférjen az összes eszköznaplójához, az app továbbra is hozzáférhet a saját naplóihoz. Előfordulhat, hogy az eszköz gyártója továbbra is hozzáfér az eszközön található bizonyos naplókhoz és adatokhoz."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne jelenjen meg újra"</string> <string name="slices_permission_request" msgid="3677129866636153406">"A(z) <xliff:g id="APP_0">%1$s</xliff:g> alkalmazás részleteket szeretne megjeleníteni a(z) <xliff:g id="APP_2">%2$s</xliff:g> alkalmazásból"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Szerkesztés"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 80586785531e..c335648726eb 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -586,16 +586,14 @@ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Էկրանի կողպում"</string> <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Շարունակելու համար ապակողպեք էկրանը"</string> <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Մատը ուժեղ սեղմեք սկաների վրա"</string> - <!-- no translation found for fingerprint_acquired_insufficient (623888149088216458) --> - <skip /> + <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Մատնահետքը չի հաջողվում ճանաչել։ Նորից փորձեք։"</string> <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Մաքրեք մատնահետքերի սկաները և նորից փորձեք"</string> <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Մաքրեք սկաները և նորից փորձեք"</string> <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Մատը ուժեղ սեղմեք սկաների վրա"</string> <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Շատ դանդաղ անցկացրիք մատը: Փորձեք նորից:"</string> <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Փորձեք մեկ այլ մատնահետք"</string> <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Շատ լուսավոր է"</string> - <!-- no translation found for fingerprint_acquired_power_press (3107864151278434961) --> - <skip /> + <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Հայտնաբերվել է սնուցման կոճակի սեղմում"</string> <string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Փորձեք փոխել մատի դիրքը"</string> <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ամեն անգամ թեթևակի փոխեք մատի դիրքը"</string> <string-array name="fingerprint_acquired_vendor"> @@ -607,15 +605,12 @@ <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Դեմքը ճանաչվեց: Սեղմեք «Հաստատել»:"</string> <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Մատնահետքի սարքն անհասանելի է:"</string> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Հնարավոր չէ կարգավորել մատնահետքը"</string> - <!-- no translation found for fingerprint_error_timeout (7361192266621252164) --> - <skip /> + <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Մատնահետքի կարգավորման ժամանակը սպառվել է։ Նորից փորձեք։"</string> <string name="fingerprint_error_canceled" msgid="540026881380070750">"Իսկորոշումը մատնահետքի միջոցով չեղարկվեց:"</string> <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Մատնահետքով նույնականացման գործողությունը չեղարկվել է օգտատիրոջ կողմից:"</string> - <!-- no translation found for fingerprint_error_lockout (6626753679019351368) --> - <skip /> + <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Չափազանց շատ փորձեր են արվել։ Օգտագործեք էկրանի կողպումը։"</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Չափազանց շատ փորձեր են արվել։ Օգտագործեք էկրանի կողպումը։"</string> - <!-- no translation found for fingerprint_error_unable_to_process (2446280592818621224) --> - <skip /> + <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Չի հաջողվում մշակել մատնահետքը։ Նորից փորձեք։"</string> <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Գրանցված մատնահետք չկա:"</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Այս սարքը չունի մատնահետքերի սկաներ։"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Տվիչը ժամանակավորապես անջատված է:"</string> @@ -1256,10 +1251,8 @@ <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Հավելվածները մեկնարկում են:"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Բեռնումն ավարտվում է:"</string> <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Դուք սեղմել եք սնուցման կոճակը։ Սովորաբար դրա արդյունքում էկրանն անջատվում է։\n\nՄատնահետքը ավելացնելու համար թեթևակի հպեք կոճակին։"</string> - <!-- no translation found for fp_power_button_enrollment_title (6976841690455338563) --> - <skip /> - <!-- no translation found for fp_power_button_enrollment_button_text (3199783266386029200) --> - <skip /> + <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Ավարտեք կարգավորումը՝ անջատելով էկրանը"</string> + <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Անջատել"</string> <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Շարունակե՞լ մատնահետքի սկանավորումը"</string> <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Դուք սեղմել եք սնուցման կոճակը։ Սովորաբար դրա արդյունքում էկրանն անջատվում է։\n\nՄատնահետքը սկանավորելու համար թեթևակի հպեք կոճակին։"</string> <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Անջատել էկրանը"</string> @@ -2057,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Հասանելի դարձնե՞լ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> հավելվածին սարքի բոլոր մատյանները"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Թույլատրել մեկանգամյա մուտքը"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Չթույլատրել"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Այն, ինչ տեղի է ունենում ձեր սարքում, գրանցվում է սարքի մատյաններում։ Հավելվածները կարող են դրանք օգտագործել անսարքությունները հայտնաբերելու և վերացնելու նպատակով։\n\nՔանի որ որոշ մատյաններ անձնական տեղեկություններ են պարունակում, խորհուրդ ենք տալիս հասանելի դարձնել ձեր սարքի բոլոր մատյանները միայն այն հավելվածներին, որոնց վստահում եք։ \n\nԵթե այս հավելվածին նման թույլտվություն չեք տվել, դրան նախկինի պես հասանելի կլինեն իր մատյանները։ Հնարավոր է՝ ձեր սարքի արտադրողին ևս հասանելի լինեն սարքի որոշ մատյաններ և տեղեկություններ։"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Այն, ինչ տեղի է ունենում ձեր սարքում, գրանցվում է սարքի մատյաններում։ Հավելվածները կարող են դրանք օգտագործել անսարքությունները հայտնաբերելու և վերացնելու նպատակով։\n\nՔանի որ որոշ մատյաններ անձնական տեղեկություններ են պարունակում, խորհուրդ ենք տալիս հասանելի դարձնել ձեր սարքի բոլոր մատյանները միայն այն հավելվածներին, որոնց վստահում եք։ \n\nԵթե այս հավելվածին նման թույլտվություն չեք տվել, դրան նախկինի պես հասանելի կլինեն իր մատյանները։ Հնարավոր է՝ ձեր սարքի արտադրողին ևս հասանելի լինեն սարքի որոշ մատյաններ և տեղեկություններ։"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Այլևս ցույց չտալ"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> հավելվածն ուզում է ցուցադրել հատվածներ <xliff:g id="APP_2">%2$s</xliff:g> հավելվածից"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Փոփոխել"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index c75b2e04bd3b..405717cbb85f 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Izinkan <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> mengakses semua log perangkat?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Izinkan akses satu kali"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Jangan izinkan"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Log perangkat merekam hal-hal yang terjadi di perangkat Anda. Aplikasi dapat menggunakan log ini untuk menemukan dan memperbaiki masalah.\n\nBeberapa log mungkin berisi info sensitif, jadi hanya izinkan aplikasi yang Anda percayai untuk mengakses semua log perangkat. \n\nJika Anda tidak mengizinkan aplikasi ini mengakses semua log perangkat, aplikasi masih dapat mengakses log-nya sendiri. Produsen perangkat masih dapat mengakses beberapa log atau info di perangkat Anda."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Log perangkat merekam hal-hal yang terjadi di perangkat Anda. Aplikasi dapat menggunakan log ini untuk menemukan dan memperbaiki masalah.\n\nBeberapa log mungkin berisi info sensitif, jadi hanya izinkan aplikasi yang Anda percayai untuk mengakses semua log perangkat. \n\nJika Anda tidak mengizinkan aplikasi ini mengakses semua log perangkat, aplikasi masih dapat mengakses log-nya sendiri. Produsen perangkat masih dapat mengakses beberapa log atau info di perangkat Anda."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Jangan tampilkan lagi"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ingin menampilkan potongan <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index ed4f28fc1725..743a21511e9d 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Veita <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> aðgang að öllum annálum í tækinu?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Leyfa aðgang í eitt skipti"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ekki leyfa"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Annálar tækisins skrá það sem gerist í tækinu. Forrit geta notað þessa annála til að finna og lagfæra vandamál.\n\nTilteknir annálar innihalda viðkvæmar upplýsingar og því skaltu einungis veita forritum sem þú treystir aðgang að öllum annálum tækisins. \n\nEf þú veitir þessu forriti ekki aðgang að öllum annálum tækisins hefur það áfram aðgang að eigin annálum. Framleiðandi tækisins getur þó hugsanlega opnað tiltekna annála eða upplýsingar í tækinu."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Annálar tækisins skrá það sem gerist í tækinu. Forrit geta notað þessa annála til að finna og lagfæra vandamál.\n\nTilteknir annálar innihalda viðkvæmar upplýsingar og því skaltu einungis veita forritum sem þú treystir aðgang að öllum annálum tækisins. \n\nEf þú veitir þessu forriti ekki aðgang að öllum annálum tækisins hefur það áfram aðgang að eigin annálum. Framleiðandi tækisins getur þó hugsanlega opnað tiltekna annála eða upplýsingar í tækinu."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ekki sýna aftur"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> vill sýna sneiðar úr <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Breyta"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 5728bd30f40a..9268cac02323 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -2051,7 +2051,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Consentire all\'app <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> di accedere a tutti i log del dispositivo?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Consenti accesso una tantum"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Non consentire"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"I log del dispositivo registrano tutto ciò che succede sul tuo dispositivo. Le app possono usare questi log per individuare problemi e correggerli.\n\nAlcuni log potrebbero contenere informazioni sensibili, quindi concedi l\'accesso a tutti i log del dispositivo soltanto alle app attendibili. \n\nSe le neghi l\'accesso a tutti i log del dispositivo, questa app può comunque accedere ai propri log. Il produttore del tuo dispositivo potrebbe essere comunque in grado di accedere ad alcuni log o informazioni sul dispositivo."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"I log del dispositivo registrano tutto ciò che succede sul tuo dispositivo. Le app possono usare questi log per individuare problemi e correggerli.\n\nAlcuni log potrebbero contenere informazioni sensibili, quindi concedi l\'accesso a tutti i log del dispositivo soltanto alle app attendibili. \n\nSe le neghi l\'accesso a tutti i log del dispositivo, questa app può comunque accedere ai propri log. Il produttore del tuo dispositivo potrebbe essere comunque in grado di accedere ad alcuni log o informazioni sul dispositivo."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Non mostrare più"</string> <string name="slices_permission_request" msgid="3677129866636153406">"L\'app <xliff:g id="APP_0">%1$s</xliff:g> vuole mostrare porzioni dell\'app <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Modifica"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 7bba4a971a86..cfc1fb0401fa 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -2052,7 +2052,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"לתת לאפליקציה <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> הרשאת גישה לכל יומני המכשיר?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"הרשאת גישה חד-פעמית"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"אין אישור"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"ביומני המכשיר מתועדת הפעילות במכשיר. האפליקציות יכולות להשתמש ביומנים האלה כדי למצוא בעיות ולפתור אותן.\n\nהמידע בחלק מהיומנים יכול להיות רגיש, לכן יש לתת הרשאת גישה לכל יומני המכשיר רק לאפליקציות מהימנות. \n\nגם אם האפליקציה הזו לא תקבל הרשאת גישה לכל יומני המכשיר, היא תוכל לגשת ליומנים שלה. יכול להיות שליצרן המכשיר עדיין תהיה גישה לחלק מהיומנים או למידע במכשיר שלך."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"ביומני המכשיר מתועדת הפעילות במכשיר. האפליקציות יכולות להשתמש ביומנים האלה כדי למצוא בעיות ולפתור אותן.\n\nהמידע בחלק מהיומנים יכול להיות רגיש, לכן יש לתת הרשאת גישה לכל יומני המכשיר רק לאפליקציות מהימנות. \n\nגם אם האפליקציה הזו לא תקבל הרשאת גישה לכל יומני המכשיר, היא תוכל לגשת ליומנים שלה. יכול להיות שליצרן המכשיר עדיין תהיה גישה לחלק מהיומנים או למידע במכשיר שלך."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"אין להציג שוב"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> רוצה להציג חלקים מ-<xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"עריכה"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 26c26b2a3d70..07e8be988393 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> にすべてのデバイスログへのアクセスを許可しますか?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"1 回限りのアクセスを許可"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"許可しない"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"デバイスのログに、このデバイスで発生したことが記録されます。アプリは問題を検出、修正するためにこれらのログを使用することができます。\n\nログによっては機密性の高い情報が含まれている可能性があるため、すべてのデバイスログへのアクセスは信頼できるアプリにのみ許可してください。\n\nすべてのデバイスログへのアクセスを許可しなかった場合も、このアプリはアプリ独自のログにアクセスできます。また、デバイスのメーカーもデバイスの一部のログや情報にアクセスできる可能性があります。"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"デバイスのログに、このデバイスで発生したことが記録されます。アプリは問題を検出、修正するためにこれらのログを使用することができます。\n\nログによっては機密性の高い情報が含まれている可能性があるため、すべてのデバイスログへのアクセスは信頼できるアプリにのみ許可してください。\n\nすべてのデバイスログへのアクセスを許可しなかった場合も、このアプリはアプリ独自のログにアクセスできます。また、デバイスのメーカーもデバイスの一部のログや情報にアクセスできる可能性があります。"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"次回から表示しない"</string> <string name="slices_permission_request" msgid="3677129866636153406">"「<xliff:g id="APP_0">%1$s</xliff:g>」が「<xliff:g id="APP_2">%2$s</xliff:g>」のスライスの表示をリクエストしています"</string> <string name="screenshot_edit" msgid="7408934887203689207">"編集"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 2818c46ce1b5..e09255f81c4b 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"გსურთ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>-ს მიანიჭოთ მოწყობილობის ყველა ჟურნალზე წვდომა?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"ერთჯერადი წვდომის დაშვება"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"არ დაიშვას"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"მოწყობილობის ჟურნალში იწერება, რა ხდება ამ მოწყობილობაზე. აპებს შეუძლია ამ ჟურნალების გამოყენება პრობლემების აღმოსაჩენად და მოსაგვარებლად.\n\nზოგი ჟურნალი შეიძლება სენსიტიური ინფორმაციის მატარებელი იყოს, ამიტომაც მოწყობილობის ყველა ჟურნალზე წვდომა მხოლოდ სანდო აპებს მიანიჭეთ. \n\nთუ ამ აპს მოწყობილობის ყველა ჟურნალზე წვდომას არ მიანიჭებთ, მას მაინც ექნება წვდომა თქვენს ჟურნალებზე. თქვენი მოწყობილობის მწარმოებელს მაინც შეეძლება თქვენი მოწყობილობის ზოგიერთ ჟურნალსა თუ ინფორმაციაზე წვდომა."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"მოწყობილობის ჟურნალში იწერება, რა ხდება ამ მოწყობილობაზე. აპებს შეუძლია ამ ჟურნალების გამოყენება პრობლემების აღმოსაჩენად და მოსაგვარებლად.\n\nზოგი ჟურნალი შეიძლება სენსიტიური ინფორმაციის მატარებელი იყოს, ამიტომაც მოწყობილობის ყველა ჟურნალზე წვდომა მხოლოდ სანდო აპებს მიანიჭეთ. \n\nთუ ამ აპს მოწყობილობის ყველა ჟურნალზე წვდომას არ მიანიჭებთ, მას მაინც ექნება წვდომა თქვენს ჟურნალებზე. თქვენი მოწყობილობის მწარმოებელს მაინც შეეძლება თქვენი მოწყობილობის ზოგიერთ ჟურნალსა თუ ინფორმაციაზე წვდომა."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"აღარ გამოჩნდეს"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>-ს სურს, გაჩვენოთ <xliff:g id="APP_2">%2$s</xliff:g>-ის ფრაგმენტები"</string> <string name="screenshot_edit" msgid="7408934887203689207">"რედაქტირება"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index c3919460e51b..4c5fe51d548b 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -77,7 +77,7 @@ <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Қоңырау шалушының жеке анықтағышы бастапқы бойынша шектелмеген. Келесі қоңырау: Шектелмеген"</string> <string name="serviceNotProvisioned" msgid="8289333510236766193">"Қызмет ұсынылмаған."</string> <string name="CLIRPermanent" msgid="166443681876381118">"Қоңырау шалушы идентификаторы параметрін өзгерту мүмкін емес."</string> - <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мобильдік деректер қызметі жоқ"</string> + <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мобильдік интернет қызметі жоқ"</string> <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Жедел қызметке қоңырау шалу қолжетімді емес"</string> <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Дауыстық қоңыраулар қызметі жоқ"</string> <string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"Дауыс қызметі немесе жедел қызметке қоңырау шалу мүмкіндігі жоқ"</string> @@ -90,7 +90,7 @@ <string name="notification_channel_network_alert" msgid="4788053066033851841">"Дабылдар"</string> <string name="notification_channel_call_forward" msgid="8230490317314272406">"Қоңырауды басқа нөмірге бағыттау"</string> <string name="notification_channel_emergency_callback" msgid="54074839059123159">"Шұғыл кері қоңырау шалу режимі"</string> - <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Мобильдік деректер күйі"</string> + <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Мобильдік интернет күйі"</string> <string name="notification_channel_sms" msgid="1243384981025535724">"SMS хабарлары"</string> <string name="notification_channel_voice_mail" msgid="8457433203106654172">"Дауыстық пошта хабарлары"</string> <string name="notification_channel_wfc" msgid="9048240466765169038">"Wi-Fi қоңыраулары"</string> @@ -1308,7 +1308,7 @@ <string name="network_switch_metered_detail" msgid="1358296010128405906">"Құрылғы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> желісінде интернетпен байланыс жоғалған жағдайда <xliff:g id="NEW_NETWORK">%1$s</xliff:g> желісін пайдаланады. Деректер ақысы алынуы мүмкін."</string> <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> желісінен <xliff:g id="NEW_NETWORK">%2$s</xliff:g> желісіне ауысты"</string> <string-array name="network_switch_type_name"> - <item msgid="2255670471736226365">"мобильдік деректер"</item> + <item msgid="2255670471736226365">"мобильдік интернет"</item> <item msgid="5520925862115353992">"Wi-Fi"</item> <item msgid="1055487873974272842">"Bluetooth"</item> <item msgid="1616528372438698248">"Ethernet"</item> @@ -1575,7 +1575,7 @@ <string name="extract_edit_menu_button" msgid="63954536535863040">"Өзгерту"</string> <string name="data_usage_warning_title" msgid="9034893717078325845">"Дерек шығыны туралы ескерту"</string> <string name="data_usage_warning_body" msgid="1669325367188029454">"Деректің <xliff:g id="APP">%s</xliff:g> пайдаландыңыз"</string> - <string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"Мобильдік деректер шегіне жетті"</string> + <string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"Мобильдік интернет шегіне жетті"</string> <string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Wi-Fi деректер шегіне жеттіңіз"</string> <string name="data_usage_limit_body" msgid="3567699582000085710">"Деректер жіберу қалған цикл үшін тоқтатылды"</string> <string name="data_usage_mobile_limit_snoozed_title" msgid="101888478915677895">"Мобильдік дерек шегінен астыңыз"</string> @@ -1583,7 +1583,7 @@ <string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"Сіз <xliff:g id="SIZE">%s</xliff:g> шегінен асып кеттіңіз"</string> <string name="data_usage_restricted_title" msgid="126711424380051268">"Фондық деректер шектелген"</string> <string name="data_usage_restricted_body" msgid="5338694433686077733">"Шектеуді жою үшін түртіңіз."</string> - <string name="data_usage_rapid_title" msgid="2950192123248740375">"Мобильдік деректер көп жұмсалды"</string> + <string name="data_usage_rapid_title" msgid="2950192123248740375">"Мобильдік интернет көп жұмсалды"</string> <string name="data_usage_rapid_body" msgid="3886676853263693432">"Қолданбаларыңыз деректерді әдеттегіден көбірек пайдаланды"</string> <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> қолданбасы деректерді әдеттегіден көбірек пайдаланды"</string> <string name="ssl_certificate" msgid="5690020361307261997">"Қауіпсіздік сертификаты"</string> @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> қолданбасына барлық құрылғының журналын пайдалануға рұқсат берілсін бе?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Бір реттік пайдалану рұқсатын беру"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Рұқсат бермеу"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Журналдарға құрылғыда не болып жатқаны жазылады. Қолданбалар осы журналдарды қате тауып, түзету үшін пайдаланады.\n\nКейбір журналдарда құпия ақпарат болуы мүмкін. Сондықтан барлық құрылғының журналын пайдалану рұқсаты тек сенімді қолданбаларға берілуі керек. \n\nБұл қолданбаға барлық құрылғының журналын пайдалануға рұқсат бермесеңіз де, ол өзінің журналдарын пайдалана береді. Құрылғы өндірушісі де құрылғыдағы кейбір журналдарды немесе ақпаратты пайдалануы мүмкін."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Журналдарға құрылғыда не болып жатқаны жазылады. Қолданбалар осы журналдарды қате тауып, түзету үшін пайдаланады.\n\nКейбір журналдарда құпия ақпарат болуы мүмкін. Сондықтан барлық құрылғының журналын пайдалану рұқсаты тек сенімді қолданбаларға берілуі керек. \n\nБұл қолданбаға барлық құрылғының журналын пайдалануға рұқсат бермесеңіз де, ол өзінің журналдарын пайдалана береді. Құрылғы өндірушісі де құрылғыдағы кейбір журналдарды немесе ақпаратты пайдалануы мүмкін."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Қайта көрсетілмесін"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> қолданбасы <xliff:g id="APP_2">%2$s</xliff:g> қолданбасының үзінділерін көрсеткісі келеді"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Өзгерту"</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 7ffd8b4d5a2f..eb8f757200f5 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"អនុញ្ញាតឱ្យ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ចូលប្រើកំណត់ហេតុឧបករណ៍ទាំងអស់ឬ?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"អនុញ្ញាតឱ្យចូលប្រើម្ដង"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"មិនអនុញ្ញាត"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"កំណត់ហេតុឧបករណ៍កត់ត្រាអ្វីដែលកើតឡើងនៅលើឧបករណ៍របស់អ្នក។ កម្មវិធីអាចប្រើកំណត់ហេតុទាំងនេះដើម្បីស្វែងរក និងដោះស្រាយបញ្ហាបាន។\n\nកំណត់ហេតុមួយចំនួនអាចមានព័ត៌មានរសើប ដូច្នេះគួរអនុញ្ញាតឱ្យចូលប្រើកំណត់ហេតុឧបករណ៍ទាំងអស់សម្រាប់តែកម្មវិធីដែលអ្នកទុកចិត្តប៉ុណ្ណោះ។ \n\nប្រសិនបើអ្នកមិនអនុញ្ញាតឱ្យកម្មវិធីនេះចូលប្រើកំណត់ហេតុឧបករណ៍ទាំងអស់ទេ វានៅតែអាចចូលប្រើកំណត់ហេតុរបស់វាផ្ទាល់បាន។ ក្រុមហ៊ុនផលិតឧបករណ៍របស់អ្នកប្រហែលជានៅតែអាចចូលប្រើកំណត់ហេតុ ឬព័ត៌មានមួយចំនួននៅលើឧបករណ៍របស់អ្នកបានដដែល។"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"កំណត់ហេតុឧបករណ៍កត់ត្រាអ្វីដែលកើតឡើងនៅលើឧបករណ៍របស់អ្នក។ កម្មវិធីអាចប្រើកំណត់ហេតុទាំងនេះដើម្បីស្វែងរក និងដោះស្រាយបញ្ហាបាន។\n\nកំណត់ហេតុមួយចំនួនអាចមានព័ត៌មានរសើប ដូច្នេះគួរអនុញ្ញាតឱ្យចូលប្រើកំណត់ហេតុឧបករណ៍ទាំងអស់សម្រាប់តែកម្មវិធីដែលអ្នកទុកចិត្តប៉ុណ្ណោះ។ \n\nប្រសិនបើអ្នកមិនអនុញ្ញាតឱ្យកម្មវិធីនេះចូលប្រើកំណត់ហេតុឧបករណ៍ទាំងអស់ទេ វានៅតែអាចចូលប្រើកំណត់ហេតុរបស់វាផ្ទាល់បាន។ ក្រុមហ៊ុនផលិតឧបករណ៍របស់អ្នកប្រហែលជានៅតែអាចចូលប្រើកំណត់ហេតុ ឬព័ត៌មានមួយចំនួននៅលើឧបករណ៍របស់អ្នកបានដដែល។"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"កុំបង្ហាញម្ដងទៀត"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ចង់បង្ហាញស្ថិតិប្រើប្រាស់របស់ <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"កែ"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 510c3d4e379e..afb98bac20d2 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"ಎಲ್ಲಾ ಸಾಧನದ ಲಾಗ್ಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ಗೆ ಅನುಮತಿಸುವುದೇ?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"ಒಂದು ಬಾರಿಯ ಪ್ರವೇಶವನ್ನು ಅನುಮತಿಸಿ"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"ಅನುಮತಿಸಬೇಡಿ"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿನ ಕಾರ್ಯಾಚರಣೆಗಳನ್ನು ಸಾಧನದ ಲಾಗ್ಗಳು ರೆಕಾರ್ಡ್ ಮಾಡುತ್ತವೆ. ಸಮಸ್ಯೆಗಳನ್ನು ಪತ್ತೆಹಚ್ಚಲು ಮತ್ತು ಪರಿಹರಿಸಲು ಆ್ಯಪ್ಗಳು ಈ ಲಾಗ್ ಅನ್ನು ಬಳಸಬಹುದು.\n\nಕೆಲವು ಲಾಗ್ಗಳು ಸೂಕ್ಷ್ಮ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಿರಬಹುದು, ಆದ್ದರಿಂದ ನಿಮ್ಮ ವಿಶ್ವಾಸಾರ್ಹ ಆ್ಯಪ್ಗಳಿಗೆ ಮಾತ್ರ ಸಾಧನದ ಎಲ್ಲಾ ಲಾಗ್ಗಳಿಗೆ ಪ್ರವೇಶವನ್ನು ಅನುಮತಿಸಿ. \n\nಎಲ್ಲಾ ಸಾಧನ ಲಾಗ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ನೀವು ಈ ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸದಿದ್ದರೆ, ಅದು ಆಗಲೂ ತನ್ನದೇ ಆದ ಲಾಗ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಬಹುದು. ನಿಮ್ಮ ಸಾಧನ ತಯಾರಕರಿಗೆ ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿನ ಕೆಲವು ಲಾಗ್ಗಳು ಅಥವಾ ಮಾಹಿತಿಯನ್ನು ಪ್ರವೇಶಿಸಲು ಈಗಲೂ ಸಾಧ್ಯವಾಗುತ್ತದೆ."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿನ ಕಾರ್ಯಾಚರಣೆಗಳನ್ನು ಸಾಧನದ ಲಾಗ್ಗಳು ರೆಕಾರ್ಡ್ ಮಾಡುತ್ತವೆ. ಸಮಸ್ಯೆಗಳನ್ನು ಪತ್ತೆಹಚ್ಚಲು ಮತ್ತು ಪರಿಹರಿಸಲು ಆ್ಯಪ್ಗಳು ಈ ಲಾಗ್ ಅನ್ನು ಬಳಸಬಹುದು.\n\nಕೆಲವು ಲಾಗ್ಗಳು ಸೂಕ್ಷ್ಮ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಿರಬಹುದು, ಆದ್ದರಿಂದ ನಿಮ್ಮ ವಿಶ್ವಾಸಾರ್ಹ ಆ್ಯಪ್ಗಳಿಗೆ ಮಾತ್ರ ಸಾಧನದ ಎಲ್ಲಾ ಲಾಗ್ಗಳಿಗೆ ಪ್ರವೇಶವನ್ನು ಅನುಮತಿಸಿ. \n\nಎಲ್ಲಾ ಸಾಧನ ಲಾಗ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ನೀವು ಈ ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸದಿದ್ದರೆ, ಅದು ಆಗಲೂ ತನ್ನದೇ ಆದ ಲಾಗ್ಗಳನ್ನು ಪ್ರವೇಶಿಸಬಹುದು. ನಿಮ್ಮ ಸಾಧನ ತಯಾರಕರಿಗೆ ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿನ ಕೆಲವು ಲಾಗ್ಗಳು ಅಥವಾ ಮಾಹಿತಿಯನ್ನು ಪ್ರವೇಶಿಸಲು ಈಗಲೂ ಸಾಧ್ಯವಾಗುತ್ತದೆ."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"ಮತ್ತೊಮ್ಮೆ ತೋರಿಸಬೇಡಿ"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_2">%2$s</xliff:g> ಸ್ಲೈಸ್ಗಳನ್ನು <xliff:g id="APP_0">%1$s</xliff:g> ತೋರಿಸಲು ಬಯಸಿದೆ"</string> <string name="screenshot_edit" msgid="7408934887203689207">"ಎಡಿಟ್"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index fc975a32248b..82ab2629cb8a 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -1165,8 +1165,8 @@ <string name="no" msgid="5122037903299899715">"취소"</string> <string name="dialog_alert_title" msgid="651856561974090712">"주의"</string> <string name="loading" msgid="3138021523725055037">"로드 중.."</string> - <string name="capital_on" msgid="2770685323900821829">"ON"</string> - <string name="capital_off" msgid="7443704171014626777">"OFF"</string> + <string name="capital_on" msgid="2770685323900821829">"사용 설정"</string> + <string name="capital_off" msgid="7443704171014626777">"사용 안함"</string> <string name="checked" msgid="9179896827054513119">"선택함"</string> <string name="not_checked" msgid="7972320087569023342">"선택 안함"</string> <string name="selected" msgid="6614607926197755875">"선택됨"</string> @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>에서 모든 기기 로그에 액세스하도록 허용하시겠습니까?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"일회성 액세스 허용"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"허용 안함"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"기기 로그에 기기에서 발생한 상황이 기록됩니다. 앱은 문제를 찾고 해결하는 데 이 로그를 사용할 수 있습니다.\n\n일부 로그는 민감한 정보를 포함할 수 있으므로 신뢰할 수 있는 앱만 모든 기기 로그에 액세스하도록 허용하세요. \n\n앱에 전체 기기 로그에 대한 액세스 권한을 부여하지 않아도 앱이 자체 로그에는 액세스할 수 있습니다. 기기 제조업체에서 일부 로그 또는 기기 내 정보에 액세스할 수도 있습니다."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"기기 로그에 기기에서 발생한 상황이 기록됩니다. 앱은 문제를 찾고 해결하는 데 이 로그를 사용할 수 있습니다.\n\n일부 로그는 민감한 정보를 포함할 수 있으므로 신뢰할 수 있는 앱만 모든 기기 로그에 액세스하도록 허용하세요. \n\n앱에 전체 기기 로그에 대한 액세스 권한을 부여하지 않아도 앱이 자체 로그에는 액세스할 수 있습니다. 기기 제조업체에서 일부 로그 또는 기기 내 정보에 액세스할 수도 있습니다."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"다시 표시 안함"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>에서 <xliff:g id="APP_2">%2$s</xliff:g>의 슬라이스를 표시하려고 합니다"</string> <string name="screenshot_edit" msgid="7408934887203689207">"수정"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 0cde52d88ce9..cdbad1854918 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> колдонмосуна түзмөктөгү бардык таржымалдарды жеткиликтүү кыласызбы?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Бир жолу жеткиликтүү кылуу"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Жок"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Түзмөктө аткарылган бардык аракеттер түзмөктүн таржымалдарында сакталып калат. Колдонмолор бул таржымалдарды колдонуп, маселелерди оңдошот.\n\nАйрым таржымалдарда купуя маалымат болушу мүмкүн, андыктан түзмөктөгү бардык таржымалдарды ишенимдүү колдонмолорго гана пайдаланууга уруксат бериңиз. \n\nЭгер бул колдонмого түзмөктөгү айрым таржымалдарга кирүүгө тыюу салсаңыз, ал өзүнүн таржымалдарын пайдалана берет. Түзмөктү өндүрүүчү түзмөгүңүздөгү айрым таржымалдарды же маалыматты көрө берет."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Түзмөктө аткарылган бардык аракеттер түзмөктүн таржымалдарында сакталып калат. Колдонмолор бул таржымалдарды колдонуп, маселелерди оңдошот.\n\nАйрым таржымалдарда купуя маалымат болушу мүмкүн, андыктан түзмөктөгү бардык таржымалдарды ишенимдүү колдонмолорго гана пайдаланууга уруксат бериңиз. \n\nЭгер бул колдонмого түзмөктөгү айрым таржымалдарга кирүүгө тыюу салсаңыз, ал өзүнүн таржымалдарын пайдалана берет. Түзмөктү өндүрүүчү түзмөгүңүздөгү айрым таржымалдарды же маалыматты көрө берет."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Экинчи көрүнбөсүн"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> колдонмосу <xliff:g id="APP_2">%2$s</xliff:g> үлгүлөрүн көрсөткөнү жатат"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Түзөтүү"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index b2677e2a1294..e5e18ccd6a1b 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"ອະນຸຍາດໃຫ້ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ເຂົ້າເຖິງບັນທຶກອຸປະກອນທັງໝົດບໍ?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"ອະນຸຍາດການເຂົ້າເຖິງແບບເທື່ອດຽວ"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"ບໍ່ອະນຸຍາດ"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"ບັນທຶກອຸປະກອນຈະບັນທຶກສິ່ງທີ່ເກີດຂຶ້ນຢູ່ອຸປະກອນຂອງທ່ານ. ແອັບສາມາດໃຊ້ບັນທຶກເຫຼົ່ານີ້ເພື່ອຊອກຫາ ແລະ ແກ້ໄຂບັນຫາໄດ້.\n\nບັນທຶກບາງຢ່າງອາດມີຂໍ້ມູນລະອຽດອ່ອນ, ດັ່ງນັ້ນໃຫ້ອະນຸຍາດສະເພາະແອັບທີ່ທ່ານເຊື່ອຖືໃຫ້ເຂົ້າເຖິງບັນທຶກອຸປະກອນທັງໝົດເທົ່ານັ້ນ. \n\nຫາກທ່ານບໍ່ອະນຸຍາດແອັບນີ້ໃຫ້ເຂົ້າເຖິງບັນທຶກອຸປະກອນທັງໝົດ, ມັນຈະຍັງຄົງສາມາດເຂົ້າເຖິງບັນທຶກຂອງຕົວມັນເອງໄດ້ຢູ່. ຜູ້ຜະລິດອຸປະກອນຂອງທ່ານອາດຍັງຄົງສາມາດເຂົ້າເຖິງບັນທຶກ ຫຼື ຂໍ້ມູນບາງຢ່າງຢູ່ອຸປະກອນຂອງທ່ານໄດ້."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"ບັນທຶກອຸປະກອນຈະບັນທຶກສິ່ງທີ່ເກີດຂຶ້ນຢູ່ອຸປະກອນຂອງທ່ານ. ແອັບສາມາດໃຊ້ບັນທຶກເຫຼົ່ານີ້ເພື່ອຊອກຫາ ແລະ ແກ້ໄຂບັນຫາໄດ້.\n\nບັນທຶກບາງຢ່າງອາດມີຂໍ້ມູນລະອຽດອ່ອນ, ດັ່ງນັ້ນໃຫ້ອະນຸຍາດສະເພາະແອັບທີ່ທ່ານເຊື່ອຖືໃຫ້ເຂົ້າເຖິງບັນທຶກອຸປະກອນທັງໝົດເທົ່ານັ້ນ. \n\nຫາກທ່ານບໍ່ອະນຸຍາດແອັບນີ້ໃຫ້ເຂົ້າເຖິງບັນທຶກອຸປະກອນທັງໝົດ, ມັນຈະຍັງຄົງສາມາດເຂົ້າເຖິງບັນທຶກຂອງຕົວມັນເອງໄດ້ຢູ່. ຜູ້ຜະລິດອຸປະກອນຂອງທ່ານອາດຍັງຄົງສາມາດເຂົ້າເຖິງບັນທຶກ ຫຼື ຂໍ້ມູນບາງຢ່າງຢູ່ອຸປະກອນຂອງທ່ານໄດ້."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"ບໍ່ຕ້ອງສະແດງອີກ"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ຕ້ອງການສະແດງ <xliff:g id="APP_2">%2$s</xliff:g> ສະໄລ້"</string> <string name="screenshot_edit" msgid="7408934887203689207">"ແກ້ໄຂ"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 99f44b27d59a..004a0a8fbb7a 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -2052,7 +2052,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Leisti „<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>“ pasiekti visus įrenginio žurnalus?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Leisti vienkartinę prieigą"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Neleisti"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Įrenginyje įrašoma, kas įvyksta jūsų įrenginyje. Programos gali naudoti šiuos žurnalus, kad surastų ir išspręstų problemas.\n\nKai kuriuose žurnaluose gali būti neskelbtinos informacijos, todėl visus įrenginio žurnalus leiskite pasiekti tik programoms, kuriomis pasitikite. \n\nJei neleisite šiai programai pasiekti visų įrenginio žurnalų, ji vis tiek galės pasiekti savo žurnalus. Įrenginio gamintojui vis tiek gali būti leidžiama pasiekti tam tikrus žurnalus ar informaciją jūsų įrenginyje."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Įrenginyje įrašoma, kas įvyksta jūsų įrenginyje. Programos gali naudoti šiuos žurnalus, kad surastų ir išspręstų problemas.\n\nKai kuriuose žurnaluose gali būti neskelbtinos informacijos, todėl visus įrenginio žurnalus leiskite pasiekti tik programoms, kuriomis pasitikite. \n\nJei neleisite šiai programai pasiekti visų įrenginio žurnalų, ji vis tiek galės pasiekti savo žurnalus. Įrenginio gamintojui vis tiek gali būti leidžiama pasiekti tam tikrus žurnalus ar informaciją jūsų įrenginyje."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Daugiau neberodyti"</string> <string name="slices_permission_request" msgid="3677129866636153406">"„<xliff:g id="APP_0">%1$s</xliff:g>“ nori rodyti „<xliff:g id="APP_2">%2$s</xliff:g>“ fragmentus"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Redaguoti"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 62ab61e67f46..ef1a95f616c4 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -2051,7 +2051,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Vai atļaujat lietotnei <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> piekļūt visiem ierīces žurnāliem?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Atļaut vienreizēju piekļuvi"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Neatļaut"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Ierīces žurnālos tiek reģistrēti ierīces procesi un notikumi. Lietotņu izstrādātāji var izmantot šos žurnālus, lai atrastu un izlabotu problēmas savās lietotnēs.\n\nDažos žurnālos var būt ietverta sensitīva informācija, tāpēc atļaujiet tikai uzticamām lietotnēm piekļūt visiem ierīces žurnāliem. \n\nJa neatļausiet šai lietotnei piekļūt visiem ierīces žurnāliem, lietotnes izstrādātājs joprojām varēs piekļūt pašas lietotnes žurnāliem. Iespējams, ierīces ražotājs joprojām varēs piekļūt noteiktiem žurnāliem vai informācijai jūsu ierīcē."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Ierīces žurnālos tiek reģistrēti ierīces procesi un notikumi. Lietotņu izstrādātāji var izmantot šos žurnālus, lai atrastu un izlabotu problēmas savās lietotnēs.\n\nDažos žurnālos var būt ietverta sensitīva informācija, tāpēc atļaujiet tikai uzticamām lietotnēm piekļūt visiem ierīces žurnāliem. \n\nJa neatļausiet šai lietotnei piekļūt visiem ierīces žurnāliem, lietotnes izstrādātājs joprojām varēs piekļūt pašas lietotnes žurnāliem. Iespējams, ierīces ražotājs joprojām varēs piekļūt noteiktiem žurnāliem vai informācijai jūsu ierīcē."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Vairs nerādīt"</string> <string name="slices_permission_request" msgid="3677129866636153406">"Lietotne <xliff:g id="APP_0">%1$s</xliff:g> vēlas rādīt lietotnes <xliff:g id="APP_2">%2$s</xliff:g> sadaļas"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Rediģēt"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 7d3cb8117685..a4ff5b8a178d 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Да се дозволи <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> да пристапува до целата евиденција на уредот?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Дозволи еднократен пристап"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Не дозволувај"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Дневниците за евиденција на уредот снимаат што се случува на вашиот уред. Апликациите може да ги користат овие дневници за евиденција за да наоѓаат и поправаат проблеми.\n\nНекои дневници за евиденција може да содржат чувствителни податоци, па затоа дозволете им пристап до сите дневници за евиденција на уредот само на апликациите во кои имате доверба. \n\nАко не ѝ дозволите на апликацијава да пристапува до сите дневници за евиденција на уредот, таа сепак ќе може да пристапува до сопствените дневници за евиденција. Производителот на вашиот уред можеби сепак ќе може да пристапува до некои дневници за евиденција или податоци на уредот."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Дневниците за евиденција на уредот снимаат што се случува на вашиот уред. Апликациите може да ги користат овие дневници за евиденција за да наоѓаат и поправаат проблеми.\n\nНекои дневници за евиденција може да содржат чувствителни податоци, па затоа дозволете им пристап до сите дневници за евиденција на уредот само на апликациите во кои имате доверба. \n\nАко не ѝ дозволите на апликацијава да пристапува до сите дневници за евиденција на уредот, таа сепак ќе може да пристапува до сопствените дневници за евиденција. Производителот на вашиот уред можеби сепак ќе може да пристапува до некои дневници за евиденција или податоци на уредот."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Не прикажувај повторно"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> сака да прикажува делови од <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Измени"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 74ab9f184a29..7f9d131102ec 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"എല്ലാ ഉപകരണ ലോഗുകളും ആക്സസ് ചെയ്യാൻ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> എന്നതിനെ അനുവദിക്കണോ?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"ഒറ്റത്തവണ ആക്സസ് അനുവദിക്കുക"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"അനുവദിക്കരുത്"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"ഉപകരണ ലോഗുകൾ നിങ്ങളുടെ ഉപകരണത്തിൽ എന്തൊക്കെയാണ് സംഭവിക്കുന്നതെന്ന് റെക്കോർഡ് ചെയ്യുന്നു. പ്രശ്നങ്ങൾ കണ്ടെത്തി പരിഹരിക്കുന്നതിന് ആപ്പുകൾക്ക് ഈ ലോഗുകൾ ഉപയോഗിക്കാൻ കഴിയും.\n\nചില ലോഗുകളിൽ സൂക്ഷ്മമായി കൈകാര്യം ചെയ്യേണ്ട വിവരങ്ങൾ അടങ്ങിയിരിക്കാൻ സാധ്യതയുള്ളതിനാൽ, എല്ലാ ഉപകരണ ലോഗുകളും ആക്സസ് ചെയ്യാനുള്ള അനുമതി നിങ്ങൾക്ക് വിശ്വാസമുള്ള ആപ്പുകൾക്ക് മാത്രം നൽകുക. \n\nഎല്ലാ ഉപകരണ ലോഗുകളും ആക്സസ് ചെയ്യാനുള്ള അനുവാദം നൽകിയില്ലെങ്കിലും, ഈ ആപ്പിന് അതിന്റെ സ്വന്തം ലോഗുകൾ ആക്സസ് ചെയ്യാനാകും. നിങ്ങളുടെ ഉപകരണ നിർമ്മാതാവിന് തുടർന്നും നിങ്ങളുടെ ഉപകരണത്തിലെ ചില ലോഗുകളോ വിവരങ്ങളോ ആക്സസ് ചെയ്യാനായേക്കും."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"ഉപകരണ ലോഗുകൾ നിങ്ങളുടെ ഉപകരണത്തിൽ എന്തൊക്കെയാണ് സംഭവിക്കുന്നതെന്ന് റെക്കോർഡ് ചെയ്യുന്നു. പ്രശ്നങ്ങൾ കണ്ടെത്തി പരിഹരിക്കുന്നതിന് ആപ്പുകൾക്ക് ഈ ലോഗുകൾ ഉപയോഗിക്കാൻ കഴിയും.\n\nചില ലോഗുകളിൽ സൂക്ഷ്മമായി കൈകാര്യം ചെയ്യേണ്ട വിവരങ്ങൾ അടങ്ങിയിരിക്കാൻ സാധ്യതയുള്ളതിനാൽ, എല്ലാ ഉപകരണ ലോഗുകളും ആക്സസ് ചെയ്യാനുള്ള അനുമതി നിങ്ങൾക്ക് വിശ്വാസമുള്ള ആപ്പുകൾക്ക് മാത്രം നൽകുക. \n\nഎല്ലാ ഉപകരണ ലോഗുകളും ആക്സസ് ചെയ്യാനുള്ള അനുവാദം നൽകിയില്ലെങ്കിലും, ഈ ആപ്പിന് അതിന്റെ സ്വന്തം ലോഗുകൾ ആക്സസ് ചെയ്യാനാകും. നിങ്ങളുടെ ഉപകരണ നിർമ്മാതാവിന് തുടർന്നും നിങ്ങളുടെ ഉപകരണത്തിലെ ചില ലോഗുകളോ വിവരങ്ങളോ ആക്സസ് ചെയ്യാനായേക്കും."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"വീണ്ടും കാണിക്കരുത്"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_2">%2$s</xliff:g> സ്ലൈസുകൾ കാണിക്കാൻ <xliff:g id="APP_0">%1$s</xliff:g> താൽപ്പര്യപ്പെടുന്നു"</string> <string name="screenshot_edit" msgid="7408934887203689207">"എഡിറ്റ് ചെയ്യുക"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 9c0e6d3faf23..ecea6c0c3ee3 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>-д төхөөрөмжийн бүх логт хандахыг зөвшөөрөх үү?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Нэг удаагийн хандалтыг зөвшөөрнө үү"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Бүү зөвшөөр"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Төхөөрөмжийн лог нь таны төхөөрөмж дээр юу болж байгааг бичдэг. Аппууд эдгээр логийг асуудлыг олох болон засахад ашиглах боломжтой.\n\nЗарим лог эмзэг мэдээлэл агуулж байж магадгүй тул та зөвхөн итгэдэг аппууддаа төхөөрөмжийн бүх логт хандахыг зөвшөөрнө үү. \n\nХэрэв та энэ аппад төхөөрөмжийн бүх логт хандахыг зөвшөөрөхгүй бол энэ нь өөрийн логт хандах боломжтой хэвээр байх болно. Tаны төхөөрөмж үйлдвэрлэгч таны төхөөрөмж дээрх зарим лог эсвэл мэдээлэлд хандах боломжтой хэвээр байж магадгүй."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Төхөөрөмжийн лог нь таны төхөөрөмж дээр юу болж байгааг бичдэг. Аппууд эдгээр логийг асуудлыг олох болон засахад ашиглах боломжтой.\n\nЗарим лог эмзэг мэдээлэл агуулж байж магадгүй тул та зөвхөн итгэдэг аппууддаа төхөөрөмжийн бүх логт хандахыг зөвшөөрнө үү. \n\nХэрэв та энэ аппад төхөөрөмжийн бүх логт хандахыг зөвшөөрөхгүй бол энэ нь өөрийн логт хандах боломжтой хэвээр байх болно. Tаны төхөөрөмж үйлдвэрлэгч таны төхөөрөмж дээрх зарим лог эсвэл мэдээлэлд хандах боломжтой хэвээр байж магадгүй."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Дахиж бүү харуул"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> <xliff:g id="APP_2">%2$s</xliff:g>-н хэсгүүдийг (slices) харуулах хүсэлтэй байна"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Засах"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index d2dbddf9295d..01989c4b77e6 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ला सर्व डिव्हाइस लॉग अॅक्सेस करण्याची अनुमती द्यायची आहे का?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"एक वेळ अॅक्सेसची अनुमती द्या"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"अनुमती देऊ नका"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"तुमच्या डिव्हाइसवर काय होते ते डिव्हाइस लॉग रेकॉर्ड करते. समस्या शोधण्यासाठी आणि त्यांचे निराकरण करण्याकरिता ॲप्स हे लॉग वापरू शकतात.\n\nकाही लॉगमध्ये संवेदनशील माहिती असू शकते, त्यामुळे फक्त तुमचा विश्वास असलेल्या ॲप्सना सर्व डिव्हाइस लॉग अॅक्सेस करण्याची अनुमती द्या. \n\nतुम्ही या ॲपला सर्व डिव्हाइस लॉग अॅक्सेस करण्याची अनुमती न दिल्यास, ते तरीही त्याचा स्वतःचा लॉग अॅक्सेस करू शकते. तुमच्या डिव्हाइसचा उत्पादक तरीही काही लॉग किंवा तुमच्या डिव्हाइसवरील माहिती अॅक्सेस करू शकतो."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"तुमच्या डिव्हाइसवर काय होते ते डिव्हाइस लॉग रेकॉर्ड करते. समस्या शोधण्यासाठी आणि त्यांचे निराकरण करण्याकरिता ॲप्स हे लॉग वापरू शकतात.\n\nकाही लॉगमध्ये संवेदनशील माहिती असू शकते, त्यामुळे फक्त तुमचा विश्वास असलेल्या ॲप्सना सर्व डिव्हाइस लॉग अॅक्सेस करण्याची अनुमती द्या. \n\nतुम्ही या ॲपला सर्व डिव्हाइस लॉग अॅक्सेस करण्याची अनुमती न दिल्यास, ते तरीही त्याचा स्वतःचा लॉग अॅक्सेस करू शकते. तुमच्या डिव्हाइसचा उत्पादक तरीही काही लॉग किंवा तुमच्या डिव्हाइसवरील माहिती अॅक्सेस करू शकतो."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"पुन्हा दाखवू नका"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ला <xliff:g id="APP_2">%2$s</xliff:g> चे तुकडे दाखवायचे आहेत"</string> <string name="screenshot_edit" msgid="7408934887203689207">"संपादित करा"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index a0c9e5adbc5e..511546d4a2f9 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Benarkan <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> mengakses semua log peranti?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Benarkan akses satu kali"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Jangan benarkan"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Log peranti merekodkan perkara yang berlaku pada peranti anda. Apl dapat menggunakan log ini untuk menemukan dan membetulkan isu.\n\nSesetengah log mungkin mengandungi maklumat sensitif, jadi benarkan apl yang anda percaya sahaja untuk mengakses semua log peranti. \n\nJika anda tidak membenarkan apl ini mengakses semua log peranti, apl masih boleh mengakses log sendiri. Pengilang peranti anda mungkin masih dapat mengakses sesetengah log atau maklumat pada peranti anda."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Log peranti merekodkan perkara yang berlaku pada peranti anda. Apl dapat menggunakan log ini untuk menemukan dan membetulkan isu.\n\nSesetengah log mungkin mengandungi maklumat sensitif, jadi benarkan apl yang anda percaya sahaja untuk mengakses semua log peranti. \n\nJika anda tidak membenarkan apl ini mengakses semua log peranti, apl masih boleh mengakses log sendiri. Pengilang peranti anda mungkin masih dapat mengakses sesetengah log atau maklumat pada peranti anda."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Jangan tunjuk lagi"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> mahu menunjukkan <xliff:g id="APP_2">%2$s</xliff:g> hirisan"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Edit"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index e4c4131c62b4..abb274023b3b 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ကို စက်မှတ်တမ်းအားလုံး သုံးခွင့်ပြုမလား။"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"တစ်ခါသုံး ဝင်ခွင့်ပေးရန်"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"ခွင့်မပြုပါ"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"သင့်စက်ရှိ အဖြစ်အပျက်များကို စက်မှတ်တမ်းများက မှတ်တမ်းတင်သည်။ အက်ပ်များက ပြဿနာများ ရှာဖွေပြီးဖြေရှင်းရန် ဤမှတ်တမ်းများကို သုံးနိုင်သည်။\n\nအချို့မှတ်တမ်းများတွင် သတိထားရမည့်အချက်အလက်များ ပါဝင်နိုင်သဖြင့် စက်မှတ်တမ်းအားလုံးကို ယုံကြည်ရသည့် အက်ပ်များကိုသာ သုံးခွင့်ပြုပါ။ \n\nဤအက်ပ်ကို စက်မှတ်တမ်းအားလုံး သုံးခွင့်မပြုသော်လည်း ၎င်းက ၎င်း၏ကိုယ်ပိုင်မှတ်တမ်းကို သုံးနိုင်ဆဲဖြစ်သည်။ သင့်စက်ရှိ အချို့မှတ်တမ်းများ (သို့) အချက်အလက်များကို သင့်စက်ထုတ်လုပ်သူက သုံးနိုင်ပါသေးသည်။"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"သင့်စက်ရှိ အဖြစ်အပျက်များကို စက်မှတ်တမ်းများက မှတ်တမ်းတင်သည်။ အက်ပ်များက ပြဿနာများ ရှာဖွေပြီးဖြေရှင်းရန် ဤမှတ်တမ်းများကို သုံးနိုင်သည်။\n\nအချို့မှတ်တမ်းများတွင် သတိထားရမည့်အချက်အလက်များ ပါဝင်နိုင်သဖြင့် စက်မှတ်တမ်းအားလုံးကို ယုံကြည်ရသည့် အက်ပ်များကိုသာ သုံးခွင့်ပြုပါ။ \n\nဤအက်ပ်ကို စက်မှတ်တမ်းအားလုံး သုံးခွင့်မပြုသော်လည်း ၎င်းက ၎င်း၏ကိုယ်ပိုင်မှတ်တမ်းကို သုံးနိုင်ဆဲဖြစ်သည်။ သင့်စက်ရှိ အချို့မှတ်တမ်းများ (သို့) အချက်အလက်များကို သင့်စက်ထုတ်လုပ်သူက သုံးနိုင်ပါသေးသည်။"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"နောက်ထပ်မပြပါနှင့်"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> သည် <xliff:g id="APP_2">%2$s</xliff:g> ၏အချပ်များကို ပြသလိုသည်"</string> <string name="screenshot_edit" msgid="7408934887203689207">"တည်းဖြတ်ရန်"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 762a03eb1c1f..90c88b6c31b2 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Vil du gi <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> tilgang til alle enhetslogger?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Gi éngangstilgang"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ikke tillat"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Enhetslogger registrerer det som skjer på enheten din. Apper kan bruke disse loggene til å finne og løse problemer.\n\nNoen logger kan inneholde sensitiv informasjon, så du bør bare gi tilgang til alle enhetslogger til apper du stoler på. \n\nHvis du ikke gir denne appen tilgang til alle enhetslogger, har den fortsatt tilgang til sine egne logger. Enhetsprodusenten kan fortsatt ha tilgang til visse logger eller noe informasjon på enheten din."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Enhetslogger registrerer det som skjer på enheten din. Apper kan bruke disse loggene til å finne og løse problemer.\n\nNoen logger kan inneholde sensitiv informasjon, så du bør bare gi tilgang til alle enhetslogger til apper du stoler på. \n\nHvis du ikke gir denne appen tilgang til alle enhetslogger, har den fortsatt tilgang til sine egne logger. Enhetsprodusenten kan fortsatt ha tilgang til visse logger eller noe informasjon på enheten din."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ikke vis igjen"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> vil vise <xliff:g id="APP_2">%2$s</xliff:g>-utsnitt"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Endre"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 1ad85c82f29b..60b0eeab1645 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> लाई डिभाइसका सबै लग हेर्ने अनुमति दिने हो?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"एक पटक प्रयोग गर्ने अनुमति दिनुहोस्"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"अनुमति नदिनुहोस्"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"डिभाइसका लगले तपाईंको डिभाइसमा भएका विभिन्न गतिविधिको अभिलेख राख्छ। एपहरू यी लगका आधारमा समस्या पत्ता लगाउन र तिनको समाधान गर्न सक्छन्।\n\nकेही लगहरूमा संवेदनशील जानकारी समावेश हुन सक्ने भएकाले आफूले भरोसा गर्ने एपलाई मात्र डिभाइसका सबै लग हेर्ने अनुमति दिनुहोस्। \n\nतपाईंले यो एपलाई डिभाइसका सबै लग हेर्ने अनुमति दिनुभएन भने पनि यसले आफ्नै लग भने हेर्न सक्छ। तपाईंको डिभाइसको उत्पादकले पनि तपाईंको डिभाइसमा भएका केही लग वा जानकारी हेर्न सक्ने सम्भावना हुन्छ।"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"डिभाइसका लगले तपाईंको डिभाइसमा भएका विभिन्न गतिविधिको अभिलेख राख्छ। एपहरू यी लगका आधारमा समस्या पत्ता लगाउन र तिनको समाधान गर्न सक्छन्।\n\nकेही लगहरूमा संवेदनशील जानकारी समावेश हुन सक्ने भएकाले आफूले भरोसा गर्ने एपलाई मात्र डिभाइसका सबै लग हेर्ने अनुमति दिनुहोस्। \n\nतपाईंले यो एपलाई डिभाइसका सबै लग हेर्ने अनुमति दिनुभएन भने पनि यसले आफ्नै लग भने हेर्न सक्छ। तपाईंको डिभाइसको उत्पादकले पनि तपाईंको डिभाइसमा भएका केही लग वा जानकारी हेर्न सक्ने सम्भावना हुन्छ।"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"फेरि नदेखाइयोस्"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ले <xliff:g id="APP_2">%2$s</xliff:g> का स्लाइसहरू देखाउन चाहन्छ"</string> <string name="screenshot_edit" msgid="7408934887203689207">"सम्पादन गर्नुहोस्"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 9b30b8b3175e..44aca78028c6 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> toegang geven tot alle apparaatlogboeken?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Eenmalige toegang toestaan"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Niet toestaan"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Apparaatlogboeken leggen vast wat er op je apparaat gebeurt. Apps kunnen deze logboeken gebruiken om problemen op te sporen en te verhelpen.\n\nSommige logboeken kunnen gevoelige informatie bevatten, dus geef alleen apps die je vertrouwt toegang tot alle apparaatlogboeken. \n\nAls je deze app geen toegang tot alle apparaatlogboeken geeft, heeft de app nog wel toegang tot de eigen logboeken. De fabrikant van je apparaat heeft misschien nog steeds toegang tot bepaalde logboeken of informatie op je apparaat."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Apparaatlogboeken leggen vast wat er op je apparaat gebeurt. Apps kunnen deze logboeken gebruiken om problemen op te sporen en te verhelpen.\n\nSommige logboeken kunnen gevoelige informatie bevatten, dus geef alleen apps die je vertrouwt toegang tot alle apparaatlogboeken. \n\nAls je deze app geen toegang tot alle apparaatlogboeken geeft, heeft de app nog wel toegang tot de eigen logboeken. De fabrikant van je apparaat heeft misschien nog steeds toegang tot bepaalde logboeken of informatie op je apparaat."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Niet opnieuw tonen"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wil segmenten van <xliff:g id="APP_2">%2$s</xliff:g> tonen"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Bewerken"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 2829730ae30a..bfe9f0ae8eee 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"ସମସ୍ତ ଡିଭାଇସ ଲଗକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"ଗୋଟିଏ-ଥର ଆକ୍ସେସ ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"ଆପଣଙ୍କ ଡିଭାଇସରେ ଯାହା ହୁଏ ତାହା ଡିଭାଇସ ଲଗଗୁଡ଼ିକ ରେକର୍ଡ କରେ। ସମସ୍ୟାଗୁଡ଼ିକୁ ଖୋଜି ସମାଧାନ କରିବାକୁ ଆପ୍ସ ଏହି ଲଗଗୁଡ଼ିକୁ ବ୍ୟବହାର କରିପାରିବ।\n\nକିଛି ଲଗରେ ସମ୍ବେଦନଶୀଳ ସୂଚନା ଥାଇପାରେ, ତେଣୁ ସମସ୍ତ ଡିଭାଇସ ଲଗକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଆପଣ ବିଶ୍ୱାସ କରୁଥିବା ଆପ୍ସକୁ ହିଁ ଅନୁମତି ଦିଅନ୍ତୁ। \n\nଯଦି ଆପଣ ସମସ୍ତ ଡିଭାଇସ ଲଗକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତି ନାହିଁ, ତେବେ ବି ଏହା ନିଜର ଡିଭାଇସ ଲଗଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିପାରିବ। ଆପଣଙ୍କ ଡିଭାଇସର ନିର୍ମାତା ଏବେ ବି ଆପଣଙ୍କର ଡିଭାଇସରେ କିଛି ଲଗ କିମ୍ବା ସୂଚନାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ସକ୍ଷମ ହୋଇପାରନ୍ତି।"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"ଆପଣଙ୍କ ଡିଭାଇସରେ ଯାହା ହୁଏ ତାହା ଡିଭାଇସ ଲଗଗୁଡ଼ିକ ରେକର୍ଡ କରେ। ସମସ୍ୟାଗୁଡ଼ିକୁ ଖୋଜି ସମାଧାନ କରିବାକୁ ଆପ୍ସ ଏହି ଲଗଗୁଡ଼ିକୁ ବ୍ୟବହାର କରିପାରିବ।\n\nକିଛି ଲଗରେ ସମ୍ବେଦନଶୀଳ ସୂଚନା ଥାଇପାରେ, ତେଣୁ ସମସ୍ତ ଡିଭାଇସ ଲଗକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଆପଣ ବିଶ୍ୱାସ କରୁଥିବା ଆପ୍ସକୁ ହିଁ ଅନୁମତି ଦିଅନ୍ତୁ। \n\nଯଦି ଆପଣ ସମସ୍ତ ଡିଭାଇସ ଲଗକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତି ନାହିଁ, ତେବେ ବି ଏହା ନିଜର ଡିଭାଇସ ଲଗଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିପାରିବ। ଆପଣଙ୍କ ଡିଭାଇସର ନିର୍ମାତା ଏବେ ବି ଆପଣଙ୍କର ଡିଭାଇସରେ କିଛି ଲଗ କିମ୍ବା ସୂଚନାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ସକ୍ଷମ ହୋଇପାରନ୍ତି।"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"ପୁଣି ଦେଖାନ୍ତୁ ନାହିଁ"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>, <xliff:g id="APP_2">%2$s</xliff:g> ସ୍ଲାଇସ୍କୁ ଦେଖାଇବା ପାଇଁ ଚାହେଁ"</string> <string name="screenshot_edit" msgid="7408934887203689207">"ଏଡିଟ କରନ୍ତୁ"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 85b9433b81f0..7cd059a11661 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"ਕੀ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ਨੂੰ ਸਾਰੇ ਡੀਵਾਈਸ ਲੌਗਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"ਇੱਕ-ਵਾਰ ਲਈ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿਓ"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"ਆਗਿਆ ਨਾ ਦਿਓ"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"ਡੀਵਾਈਸ ਲੌਗਾਂ ਵਿੱਚ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀਆਂ ਕਾਰਵਾਈਆਂ ਰਿਕਾਰਡ ਹੁੰਦੀਆਂ ਹਨ। ਐਪਾਂ ਸਮੱਸਿਆਵਾਂ ਨੂੰ ਲੱਭਣ ਅਤੇ ਉਨ੍ਹਾਂ ਦਾ ਹੱਲ ਕਰਨ ਲਈ ਇਨ੍ਹਾਂ ਲੌਗਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਸਕਦੀਆਂ ਹਨ।\n\nਕੁਝ ਲੌਗਾਂ ਵਿੱਚ ਸੰਵੇਦਨਸ਼ੀਲ ਜਾਣਕਾਰੀ ਸ਼ਾਮਲ ਹੋ ਸਕਦੀ ਹੈ, ਇਸ ਲਈ ਸਿਰਫ਼ ਆਪਣੀਆਂ ਭਰੋਸੇਯੋਗ ਐਪਾਂ ਨੂੰ ਹੀ ਸਾਰੇ ਡੀਵਾਈਸ ਲੌਗਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ। \n\nਜੇ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਸਾਰੇ ਡੀਵਾਈਸ ਲੌਗਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੰਦੇ ਹੋ, ਤਾਂ ਇਹ ਹਾਲੇ ਵੀ ਆਪਣੇ ਲੌਗਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦੀ ਹੈ। ਤੁਹਾਡਾ ਡੀਵਾਈਸ ਨਿਰਮਾਤਾ ਹਾਲੇ ਵੀ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਮੌਜੂਦ ਕੁਝ ਲੌਗਾਂ ਜਾਂ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦਾ ਹੈ।"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"ਡੀਵਾਈਸ ਲੌਗਾਂ ਵਿੱਚ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀਆਂ ਕਾਰਵਾਈਆਂ ਰਿਕਾਰਡ ਹੁੰਦੀਆਂ ਹਨ। ਐਪਾਂ ਸਮੱਸਿਆਵਾਂ ਨੂੰ ਲੱਭਣ ਅਤੇ ਉਨ੍ਹਾਂ ਦਾ ਹੱਲ ਕਰਨ ਲਈ ਇਨ੍ਹਾਂ ਲੌਗਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਸਕਦੀਆਂ ਹਨ।\n\nਕੁਝ ਲੌਗਾਂ ਵਿੱਚ ਸੰਵੇਦਨਸ਼ੀਲ ਜਾਣਕਾਰੀ ਸ਼ਾਮਲ ਹੋ ਸਕਦੀ ਹੈ, ਇਸ ਲਈ ਸਿਰਫ਼ ਆਪਣੀਆਂ ਭਰੋਸੇਯੋਗ ਐਪਾਂ ਨੂੰ ਹੀ ਸਾਰੇ ਡੀਵਾਈਸ ਲੌਗਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ। \n\nਜੇ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਸਾਰੇ ਡੀਵਾਈਸ ਲੌਗਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੰਦੇ ਹੋ, ਤਾਂ ਇਹ ਹਾਲੇ ਵੀ ਆਪਣੇ ਲੌਗਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦੀ ਹੈ। ਤੁਹਾਡਾ ਡੀਵਾਈਸ ਨਿਰਮਾਤਾ ਹਾਲੇ ਵੀ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਮੌਜੂਦ ਕੁਝ ਲੌਗਾਂ ਜਾਂ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦਾ ਹੈ।"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"ਦੁਬਾਰਾ ਨਾ ਦਿਖਾਓ"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ਦੀ <xliff:g id="APP_2">%2$s</xliff:g> ਦੇ ਹਿੱਸੇ ਦਿਖਾਉਣ ਦੀ ਇੱਛਾ ਹੈ"</string> <string name="screenshot_edit" msgid="7408934887203689207">"ਸੰਪਾਦਨ ਕਰੋ"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 73ccd5a7f38a..974b6f6dcfae 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -2052,7 +2052,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Zezwolić aplikacji <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> na dostęp do wszystkich dzienników urządzenia?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Zezwól na jednorazowy dostęp"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nie zezwalaj"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Dzienniki urządzenia zapisują, co dzieje się na urządzeniu. Aplikacje mogą ich używać do wykrywania i rozwiązywania problemów.\n\nNiektóre dzienniki mogą zawierać poufne dane, dlatego na dostęp do wszystkich dzienników zezwalaj tylko aplikacjom, którym ufasz. \n\nNawet jeśli nie zezwolisz tej aplikacji na dostęp do wszystkich dzienników na urządzeniu, będzie mogła korzystać z własnych. Producent urządzenia nadal będzie mógł używać niektórych dzienników na urządzeniu."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Dzienniki urządzenia zapisują, co dzieje się na urządzeniu. Aplikacje mogą ich używać do wykrywania i rozwiązywania problemów.\n\nNiektóre dzienniki mogą zawierać poufne dane, dlatego na dostęp do wszystkich dzienników zezwalaj tylko aplikacjom, którym ufasz. \n\nNawet jeśli nie zezwolisz tej aplikacji na dostęp do wszystkich dzienników na urządzeniu, będzie mogła korzystać z własnych. Producent urządzenia nadal będzie mógł używać niektórych dzienników na urządzeniu."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Nie pokazuj ponownie"</string> <string name="slices_permission_request" msgid="3677129866636153406">"Aplikacja <xliff:g id="APP_0">%1$s</xliff:g> chce pokazywać wycinki z aplikacji <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Edytuj"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 1bcb2ec32e35..0c3a8f945e7f 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -2051,7 +2051,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Permitir que o app <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> acesse todos os registros do dispositivo?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permitir o acesso único"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Não permitir"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Os registros do dispositivo gravam o que acontece nele. Os apps podem usar esses registros para encontrar e corrigir problemas.\n\nAlguns registros podem conter informações sensíveis, então autorize o acesso a eles apenas para os apps em que você confia. \n\nSe você não permitir que esse app acesse todos os registros do dispositivo, ele ainda vai poder acessar os próprios. O fabricante do dispositivo também pode ter acesso a alguns registros ou informações."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Os registros do dispositivo gravam o que acontece nele. Os apps podem usar esses registros para encontrar e corrigir problemas.\n\nAlguns registros podem conter informações sensíveis, então autorize o acesso a eles apenas para os apps em que você confia. \n\nSe você não permitir que esse app acesse todos os registros do dispositivo, ele ainda vai poder acessar os próprios. O fabricante do dispositivo também pode ter acesso a alguns registros ou informações."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Não mostrar novamente"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> quer mostrar partes do app <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Editar"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index fb61ef3d1114..a99f49a81558 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -2051,7 +2051,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Permitir que a app <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> aceda a todos os registos do dispositivo?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permitir acesso único"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Não permitir"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Os registos do dispositivo documentam o que ocorre no seu dispositivo. As apps podem usar esses registos para detetar e corrigir problemas.\n\nAlguns registos podem conter informações confidenciais e, por isso, o acesso a todos os registos do dispositivo deve apenas ser permitido às apps nas quais confia. \n\nSe não permitir o acesso desta app a todos os registos do dispositivo, esta pode ainda assim aceder aos próprios registos. O fabricante do dispositivo pode continuar a aceder a alguns registos ou informações no seu dispositivo."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Os registos do dispositivo documentam o que ocorre no seu dispositivo. As apps podem usar esses registos para detetar e corrigir problemas.\n\nAlguns registos podem conter informações confidenciais e, por isso, o acesso a todos os registos do dispositivo deve apenas ser permitido às apps nas quais confia. \n\nSe não permitir o acesso desta app a todos os registos do dispositivo, esta pode ainda assim aceder aos próprios registos. O fabricante do dispositivo pode continuar a aceder a alguns registos ou informações no seu dispositivo."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Não mostrar de novo"</string> <string name="slices_permission_request" msgid="3677129866636153406">"A app <xliff:g id="APP_0">%1$s</xliff:g> pretende mostrar partes da app <xliff:g id="APP_2">%2$s</xliff:g>."</string> <string name="screenshot_edit" msgid="7408934887203689207">"Editar"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 1bcb2ec32e35..0c3a8f945e7f 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -2051,7 +2051,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Permitir que o app <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> acesse todos os registros do dispositivo?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permitir o acesso único"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Não permitir"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Os registros do dispositivo gravam o que acontece nele. Os apps podem usar esses registros para encontrar e corrigir problemas.\n\nAlguns registros podem conter informações sensíveis, então autorize o acesso a eles apenas para os apps em que você confia. \n\nSe você não permitir que esse app acesse todos os registros do dispositivo, ele ainda vai poder acessar os próprios. O fabricante do dispositivo também pode ter acesso a alguns registros ou informações."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Os registros do dispositivo gravam o que acontece nele. Os apps podem usar esses registros para encontrar e corrigir problemas.\n\nAlguns registros podem conter informações sensíveis, então autorize o acesso a eles apenas para os apps em que você confia. \n\nSe você não permitir que esse app acesse todos os registros do dispositivo, ele ainda vai poder acessar os próprios. O fabricante do dispositivo também pode ter acesso a alguns registros ou informações."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Não mostrar novamente"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> quer mostrar partes do app <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Editar"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 58701d96631f..24615eeb70ed 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -34,7 +34,7 @@ <string name="defaultMsisdnAlphaTag" msgid="2285034592902077488">"MSISDN1"</string> <string name="mmiError" msgid="2862759606579822246">"Problemă de conexiune sau cod MMI nevalid."</string> <string name="mmiFdnError" msgid="3975490266767565852">"Operația este limitată la numerele cu apelări restricționate."</string> - <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"Nu puteți schimba setările de redirecționare a apelurilor de pe telefon când sunteți în roaming."</string> + <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"Nu poți schimba setările de redirecționare a apelurilor de pe telefon când ești în roaming."</string> <string name="serviceEnabled" msgid="7549025003394765639">"Serviciul a fost activat."</string> <string name="serviceEnabledFor" msgid="1463104778656711613">"Serviciul a fost activat pentru:"</string> <string name="serviceDisabled" msgid="641878791205871379">"Serviciul a fost dezactivat."</string> @@ -44,21 +44,21 @@ <string name="mmiComplete" msgid="6341884570892520140">"MMI finalizat."</string> <string name="badPin" msgid="888372071306274355">"Codul PIN vechi introdus nu este corect."</string> <string name="badPuk" msgid="4232069163733147376">"Codul PUK introdus nu este corect."</string> - <string name="mismatchPin" msgid="2929611853228707473">"Codurile PIN introduse nu se potrivesc."</string> + <string name="mismatchPin" msgid="2929611853228707473">"PIN-urile introduse nu sunt identice."</string> <string name="invalidPin" msgid="7542498253319440408">"Introdu un cod PIN alcătuit din 4 până la 8 cifre."</string> <string name="invalidPuk" msgid="8831151490931907083">"Introdu un cod PUK care să aibă 8 cifre sau mai mult."</string> <string name="needPuk" msgid="7321876090152422918">"Cardul SIM este blocat cu codul PUK. Introdu codul PUK pentru a-l debloca."</string> <string name="needPuk2" msgid="7032612093451537186">"Introdu codul PUK2 pentru a debloca cardul SIM."</string> - <string name="enablePin" msgid="2543771964137091212">"Operațiunea nu a reușit. Activați blocarea cardului SIM/RUIM."</string> + <string name="enablePin" msgid="2543771964137091212">"Operațiunea nu a reușit. Activează blocarea cardului SIM/RUIM."</string> <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584"> - <item quantity="few">V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> încercări până la blocarea cardului SIM.</item> - <item quantity="other">V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> de încercări până la blocarea cardului SIM.</item> - <item quantity="one">V-a mai rămas <xliff:g id="NUMBER_0">%d</xliff:g> încercare până la blocarea cardului SIM.</item> + <item quantity="few">Ți-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> încercări până la blocarea cardului SIM.</item> + <item quantity="other">Ți-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> de încercări până la blocarea cardului SIM.</item> + <item quantity="one">Ți-a mai rămas <xliff:g id="NUMBER_0">%d</xliff:g> încercare până la blocarea cardului SIM.</item> </plurals> <string name="imei" msgid="2157082351232630390">"IMEI"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> <string name="ClipMmi" msgid="4110549342447630629">"ID apelant de primire"</string> - <string name="ClirMmi" msgid="6752346475055446417">"Ascundeți ID-ul apelantului"</string> + <string name="ClirMmi" msgid="6752346475055446417">"Ascunde ID-ul apelantului"</string> <string name="ColpMmi" msgid="4736462893284419302">"ID-ul liniei conectate"</string> <string name="ColrMmi" msgid="5889782479745764278">"Restricționarea ID-ului liniei conectate"</string> <string name="CfMmi" msgid="8390012691099787178">"Redirecționarea apelurilor"</string> @@ -71,13 +71,13 @@ <string name="ThreeWCMmi" msgid="2436550866139999411">"Apelare de tip conferință"</string> <string name="RuacMmi" msgid="1876047385848991110">"Respingere apeluri supărătoare nedorite"</string> <string name="CndMmi" msgid="185136449405618437">"Se apelează serviciul de furnizare a numerelor"</string> - <string name="DndMmi" msgid="8797375819689129800">"Nu deranjați"</string> + <string name="DndMmi" msgid="8797375819689129800">"Nu deranja"</string> <string name="CLIRDefaultOnNextCallOn" msgid="4511621022859867988">"ID-ul apelantului este restricționat în mod prestabilit. Apelul următor: restricționat"</string> <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID-ul apelantului este restricționat în mod prestabilit. Apelul următor: nerestricționat"</string> <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID-ul apelantului este nerestricționat în mod prestabilit. Apelul următor: Restricționat."</string> <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID-ul apelantului este nerestricționat în mod prestabilit. Apelul următor: nerestricționat"</string> <string name="serviceNotProvisioned" msgid="8289333510236766193">"Nu se asigură accesul la acest serviciu."</string> - <string name="CLIRPermanent" msgid="166443681876381118">"Nu puteți să modificați setarea pentru ID-ul apelantului."</string> + <string name="CLIRPermanent" msgid="166443681876381118">"Nu poți modifica setarea pentru ID-ul apelantului."</string> <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Fără serviciu de date mobile"</string> <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Apelurile de urgență nu sunt disponibile"</string> <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Fără servicii vocale"</string> @@ -85,9 +85,9 @@ <string name="RestrictedStateContent" msgid="7693575344608618926">"Dezactivat temporar de operator"</string> <string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"Dezactivat temporar de operator pentru numărul de card SIM <xliff:g id="SIMNUMBER">%d</xliff:g>"</string> <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Nu se poate stabili conexiunea la rețeaua mobilă"</string> - <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Încercați să schimbați rețeaua preferată. Atingeți pentru a schimba."</string> + <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Încearcă să schimbi rețeaua preferată. Atinge pentru a schimba."</string> <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Apelurile de urgență nu sunt disponibile"</string> - <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Nu puteți efectua apeluri de urgență prin Wi-Fi"</string> + <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Nu poți face apeluri de urgență prin Wi-Fi"</string> <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alerte"</string> <string name="notification_channel_call_forward" msgid="8230490317314272406">"Redirecționarea apelurilor"</string> <string name="notification_channel_emergency_callback" msgid="54074839059123159">"Mod de apelare inversă de urgență"</string> @@ -125,10 +125,10 @@ <string name="roamingTextSearching" msgid="5323235489657753486">"Se caută serviciul"</string> <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Nu s-a putut configura apelarea prin Wi-Fi"</string> <string-array name="wfcOperatorErrorAlertMessages"> - <item msgid="468830943567116703">"Pentru a efectua apeluri și a trimite mesaje prin Wi-Fi, mai întâi solicitați configurarea acestui serviciu la operator. Apoi, activați din nou apelarea prin Wi-Fi din Setări. (Cod de eroare: <xliff:g id="CODE">%1$s</xliff:g>)"</item> + <item msgid="468830943567116703">"Pentru a face apeluri și a trimite mesaje prin Wi-Fi, mai întâi solicită configurarea acestui serviciu la operator. Apoi, activează din nou apelarea prin Wi-Fi din Setări. (Cod de eroare: <xliff:g id="CODE">%1$s</xliff:g>)"</item> </string-array> <string-array name="wfcOperatorErrorNotificationMessages"> - <item msgid="4795145070505729156">"A apărut o problemă la înregistrarea apelării prin Wi‑Fi la operatorul dvs.: <xliff:g id="CODE">%1$s</xliff:g>"</item> + <item msgid="4795145070505729156">"A apărut o problemă la înregistrarea apelării prin Wi‑Fi la operatorul tău: <xliff:g id="CODE">%1$s</xliff:g>"</item> </string-array> <!-- no translation found for wfcSpnFormat_spn (2982505428519096311) --> <skip /> @@ -144,8 +144,8 @@ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Apelare prin Wi-Fi"</string> <string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string> <string name="wifi_calling_off_summary" msgid="5626710010766902560">"Dezactivată"</string> - <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Apelați prin Wi-Fi"</string> - <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Apelați prin rețeaua mobilă"</string> + <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Apelează prin Wi-Fi"</string> + <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Sună prin rețeaua mobilă"</string> <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Numai Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> @@ -164,7 +164,7 @@ <string name="httpErrorAuth" msgid="469553140922938968">"Nu s-a realizat autentificarea."</string> <string name="httpErrorProxyAuth" msgid="7229662162030113406">"Autentificarea prin intermediul serverului proxy nu a reușit."</string> <string name="httpErrorConnect" msgid="3295081579893205617">"Nu s-a putut stabili conexiunea cu serverul."</string> - <string name="httpErrorIO" msgid="3860318696166314490">"Nu s-a putut efectua comunicarea cu serverul. Încercați din nou mai târziu."</string> + <string name="httpErrorIO" msgid="3860318696166314490">"Nu s-a putut efectua comunicarea cu serverul. Încearcă din nou mai târziu."</string> <string name="httpErrorTimeout" msgid="7446272815190334204">"Conexiunea la server a expirat."</string> <string name="httpErrorRedirectLoop" msgid="8455757777509512098">"Pagina conține prea multe redirecționări de server."</string> <string name="httpErrorUnsupportedScheme" msgid="2664108769858966374">"Protocolul nu este acceptat."</string> @@ -172,28 +172,28 @@ <string name="httpErrorBadUrl" msgid="754447723314832538">"Pagina nu a putut fi deschisă, deoarece adresa URL nu este validă."</string> <string name="httpErrorFile" msgid="3400658466057744084">"Fișierul nu a putut fi accesat."</string> <string name="httpErrorFileNotFound" msgid="5191433324871147386">"Nu s-a putut găsi fișierul solicitat."</string> - <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"Există prea multe solicitări în curs de procesare. Încercați din nou mai târziu."</string> + <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"Există prea multe solicitări în curs de procesare. Încearcă din nou mai târziu."</string> <string name="notification_title" msgid="5783748077084481121">"Eroare de conectare pentru <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string> <string name="contentServiceSync" msgid="2341041749565687871">"Sincronizare"</string> <string name="contentServiceSyncNotificationTitle" msgid="5766411446676388623">"Nu se poate sincroniza"</string> - <string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"Ați încercat să ștergeți prea multe <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string> - <string name="low_memory" product="tablet" msgid="5557552311566179924">"Stocarea pe tabletă este plină. Ștergeți câteva fișiere pentru a elibera spațiu."</string> - <string name="low_memory" product="watch" msgid="3479447988234030194">"Spațiul de stocare de pe ceas este plin! Ștergeți câteva fișiere pentru a elibera spațiu."</string> - <string name="low_memory" product="tv" msgid="6663680413790323318">"Spațiul de stocare de pe dispozitivul Android TV este plin. Ștergeți câteva fișiere pentru a elibera spațiu."</string> - <string name="low_memory" product="default" msgid="2539532364144025569">"Stocarea pe telefon este plină. Ștergeți câteva fișiere pentru a elibera spațiu."</string> + <string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"Ai încercat să ștergi prea multe <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string> + <string name="low_memory" product="tablet" msgid="5557552311566179924">"Stocarea pe tabletă este plină. Șterge câteva fișiere pentru a elibera spațiu."</string> + <string name="low_memory" product="watch" msgid="3479447988234030194">"Spațiul de stocare de pe ceas este plin! Șterge câteva fișiere pentru a elibera spațiu."</string> + <string name="low_memory" product="tv" msgid="6663680413790323318">"Spațiul de stocare de pe dispozitivul Android TV este plin. Șterge câteva fișiere pentru a elibera spațiu."</string> + <string name="low_memory" product="default" msgid="2539532364144025569">"Stocarea pe telefon este plină. Șterge câteva fișiere pentru a elibera spațiu."</string> <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{S-a instalat o autoritate de certificare}few{S-au instalat autorități de certificare}other{S-au instalat autorități de certificare}}"</string> <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"De o terță parte necunoscută"</string> - <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"De administratorul profilului dvs. de serviciu"</string> + <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"De administratorul profilului de serviciu"</string> <string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"De <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string> <string name="work_profile_deleted" msgid="5891181538182009328">"Profilul de serviciu a fost șters"</string> - <string name="work_profile_deleted_details" msgid="3773706828364418016">"Aplicația de administrare a profilului de serviciu lipsește sau este deteriorată. Prin urmare, profilul de serviciu și datele asociate au fost șterse. Pentru asistență, contactați administratorul."</string> + <string name="work_profile_deleted_details" msgid="3773706828364418016">"Aplicația de administrare a profilului de serviciu lipsește sau este deteriorată. Prin urmare, profilul de serviciu și datele asociate au fost șterse. Pentru asistență, contactează administratorul."</string> <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Profilul de serviciu nu mai este disponibil pe acest dispozitiv"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Prea multe încercări de introducere a parolei"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administratorul a retras dispozitivul pentru uz personal"</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Dispozitivul este gestionat"</string> - <string name="network_logging_notification_text" msgid="1327373071132562512">"Organizația dvs. gestionează acest dispozitiv și poate monitoriza traficul în rețea. Atingeți pentru mai multe detalii."</string> - <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplicațiile vă pot accesa locația"</string> - <string name="location_changed_notification_text" msgid="7158423339982706912">"Contactați administratorul IT pentru a afla mai multe"</string> + <string name="network_logging_notification_text" msgid="1327373071132562512">"Organizația ta gestionează acest dispozitiv și poate monitoriza traficul în rețea. Atinge pentru mai multe detalii."</string> + <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplicațiile îți pot accesa locația"</string> + <string name="location_changed_notification_text" msgid="7158423339982706912">"Contactează administratorul IT pentru a afla mai multe"</string> <string name="geofencing_service" msgid="3826902410740315456">"Serviciul de delimitare geografică"</string> <string name="country_detector" msgid="7023275114706088854">"Detector de țară"</string> <string name="location_service" msgid="2439187616018455546">"Servicii de localizare"</string> @@ -204,21 +204,21 @@ <string name="device_policy_manager_service" msgid="5085762851388850332">"Serviciul Manager de politici pentru dispozitive"</string> <string name="music_recognition_manager_service" msgid="7481956037950276359">"Serviciu de gestionare a recunoașterii de melodii"</string> <string name="factory_reset_warning" msgid="6858705527798047809">"Datele de pe dispozitiv vor fi șterse"</string> - <string name="factory_reset_message" msgid="2657049595153992213">"Aplicația de administrare nu poate fi utilizată. Dispozitivul va fi șters.\n\nDacă aveți întrebări, contactați administratorul organizației dvs."</string> + <string name="factory_reset_message" msgid="2657049595153992213">"Aplicația de administrare nu poate fi folosită. Dispozitivul va fi șters.\n\nDacă ai întrebări, contactează administratorul organizației."</string> <string name="printing_disabled_by" msgid="3517499806528864633">"Printare dezactivată de <xliff:g id="OWNER_APP">%s</xliff:g>."</string> - <string name="personal_apps_suspension_title" msgid="7561416677884286600">"Activați profilul de serviciu"</string> - <string name="personal_apps_suspension_text" msgid="6115455688932935597">"Aplicațiile personale sunt blocate până când activați profilul de serviciu"</string> + <string name="personal_apps_suspension_title" msgid="7561416677884286600">"Activează profilul de serviciu"</string> + <string name="personal_apps_suspension_text" msgid="6115455688932935597">"Aplicațiile personale sunt blocate până când activezi profilul de serviciu"</string> <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"Aplicațiile personale vor fi blocate pe <xliff:g id="DATE">%1$s</xliff:g>, la <xliff:g id="TIME">%2$s</xliff:g>. Administratorul IT nu permite ca profilul de serviciu să fie dezactivat mai mult de <xliff:g id="NUMBER">%3$d</xliff:g> zile."</string> - <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"Activați"</string> + <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"Activează"</string> <string name="me" msgid="6207584824693813140">"Eu"</string> <string name="power_dialog" product="tablet" msgid="8333207765671417261">"Opțiuni tablet PC"</string> <string name="power_dialog" product="tv" msgid="7792839006640933763">"Opțiuni pentru Android TV"</string> <string name="power_dialog" product="default" msgid="1107775420270203046">"Opțiuni telefon"</string> <string name="silent_mode" msgid="8796112363642579333">"Mod Silențios"</string> - <string name="turn_on_radio" msgid="2961717788170634233">"Activați funcția wireless"</string> - <string name="turn_off_radio" msgid="7222573978109933360">"Dezactivați funcția wireless"</string> - <string name="screen_lock" msgid="2072642720826409809">"Blocați ecranul"</string> - <string name="power_off" msgid="4111692782492232778">"Opriți"</string> + <string name="turn_on_radio" msgid="2961717788170634233">"Activează funcția wireless"</string> + <string name="turn_off_radio" msgid="7222573978109933360">"Dezactivează funcția wireless"</string> + <string name="screen_lock" msgid="2072642720826409809">"Blochează ecranul"</string> + <string name="power_off" msgid="4111692782492232778">"Oprește"</string> <string name="silent_mode_silent" msgid="5079789070221150912">"Sonerie dezactivată"</string> <string name="silent_mode_vibrate" msgid="8821830448369552678">"Vibrare sonerie"</string> <string name="silent_mode_ring" msgid="6039011004781526678">"Sonerie activată"</string> @@ -229,32 +229,32 @@ <string name="reboot_to_reset_title" msgid="2226229680017882787">"Revenire la setările din fabrică"</string> <string name="reboot_to_reset_message" msgid="3347690497972074356">"Se repornește…"</string> <string name="shutdown_progress" msgid="5017145516412657345">"Se închide..."</string> - <string name="shutdown_confirm" product="tablet" msgid="2872769463279602432">"Computerul dvs. tablet PC se va închide."</string> + <string name="shutdown_confirm" product="tablet" msgid="2872769463279602432">"Tableta se va închide."</string> <string name="shutdown_confirm" product="tv" msgid="7975942887313518330">"Dispozitivul Android TV se va închide."</string> - <string name="shutdown_confirm" product="watch" msgid="2977299851200240146">"Ceasul dvs. se va închide."</string> - <string name="shutdown_confirm" product="default" msgid="136816458966692315">"Telefonul dvs. se va închide."</string> - <string name="shutdown_confirm_question" msgid="796151167261608447">"Doriți să închideți?"</string> - <string name="reboot_safemode_title" msgid="5853949122655346734">"Reporniți în modul sigur"</string> - <string name="reboot_safemode_confirm" msgid="1658357874737219624">"Doriți să reporniți în modul sigur? Astfel vor fi dezactivate toate aplicațiile terță parte pe care le-ați instalat. Acestea vor fi restabilite când reporniți din nou."</string> + <string name="shutdown_confirm" product="watch" msgid="2977299851200240146">"Ceasul se va închide."</string> + <string name="shutdown_confirm" product="default" msgid="136816458966692315">"Telefonul se va închide."</string> + <string name="shutdown_confirm_question" msgid="796151167261608447">"Vrei să închizi?"</string> + <string name="reboot_safemode_title" msgid="5853949122655346734">"Repornește în modul sigur"</string> + <string name="reboot_safemode_confirm" msgid="1658357874737219624">"Repornești în modul sigur? Astfel vor fi dezactivate toate aplicațiile terță parte instalate. Acestea vor fi restabilite când repornești dispozitivul."</string> <string name="recent_tasks_title" msgid="8183172372995396653">"Recente"</string> <string name="no_recent_tasks" msgid="9063946524312275906">"Nu există aplicații recente."</string> <string name="global_actions" product="tablet" msgid="4412132498517933867">"Opțiuni tablet PC"</string> <string name="global_actions" product="tv" msgid="3871763739487450369">"Opțiuni pentru Android TV"</string> <string name="global_actions" product="default" msgid="6410072189971495460">"Opțiuni telefon"</string> - <string name="global_action_lock" msgid="6949357274257655383">"Blocați ecranul"</string> - <string name="global_action_power_off" msgid="4404936470711393203">"Opriți"</string> + <string name="global_action_lock" msgid="6949357274257655383">"Blochează ecranul"</string> + <string name="global_action_power_off" msgid="4404936470711393203">"Oprește"</string> <string name="global_action_power_options" msgid="1185286119330160073">"Alimentare"</string> - <string name="global_action_restart" msgid="4678451019561687074">"Reporniți"</string> + <string name="global_action_restart" msgid="4678451019561687074">"Repornește"</string> <string name="global_action_emergency" msgid="1387617624177105088">"Urgență"</string> <string name="global_action_bug_report" msgid="5127867163044170003">"Raport despre erori"</string> - <string name="global_action_logout" msgid="6093581310002476511">"Încheiați sesiunea"</string> + <string name="global_action_logout" msgid="6093581310002476511">"Încheie sesiunea"</string> <string name="global_action_screenshot" msgid="2610053466156478564">"Instantaneu"</string> <string name="bugreport_title" msgid="8549990811777373050">"Raport de eroare"</string> - <string name="bugreport_message" msgid="5212529146119624326">"Acest raport va colecta informații despre starea actuală a dispozitivului, pentru a le trimite într-un e-mail. Aveți răbdare după pornirea raportului despre erori până când va fi gata de trimis."</string> + <string name="bugreport_message" msgid="5212529146119624326">"Acest raport va colecta informații despre starea actuală a dispozitivului, pentru a le trimite într-un e-mail. Ai răbdare după pornirea raportului despre erori până când va fi gata de trimis."</string> <string name="bugreport_option_interactive_title" msgid="7968287837902871289">"Raport interactiv"</string> - <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Folosiți această opțiune în majoritatea situațiilor. Astfel, puteți să urmăriți progresul raportului, să introduceți mai multe detalii în privința problemei și să creați capturi de ecran. Pot fi omise unele secțiuni mai puțin folosite pentru care raportarea durează prea mult."</string> + <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Folosește această opțiune în majoritatea situațiilor. Astfel, poți să urmărești progresul raportului, să introduci mai multe detalii în privința problemei și să creezi capturi de ecran. Pot fi omise unele secțiuni mai puțin folosite pentru care raportarea durează prea mult."</string> <string name="bugreport_option_full_title" msgid="7681035745950045690">"Raport complet"</string> - <string name="bugreport_option_full_summary" msgid="1975130009258435885">"Folosiți această opțiune pentru a reduce la minimum interferențele cu sistemul când dispozitivul nu răspunde, funcționează prea lent sau când aveți nevoie de toate secțiunile raportului. Nu puteți să introduceți mai multe detalii sau să creați capturi de ecran suplimentare."</string> + <string name="bugreport_option_full_summary" msgid="1975130009258435885">"Folosește această opțiune pentru a reduce la minimum interferențele cu sistemul când dispozitivul nu răspunde, funcționează prea lent sau când ai nevoie de toate secțiunile raportului. Nu poți să introduci mai multe detalii sau să creezi capturi de ecran suplimentare."</string> <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Peste # secundă se va realiza o captură de ecran pentru raportul de eroare.}few{Peste # secunde se va realiza o captură de ecran pentru raportul de eroare.}other{Peste # de secunde se va realiza o captură de ecran pentru raportul de eroare.}}"</string> <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"S-a realizat captura de ecran a raportului de eroare"</string> <string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"Nu s-a realizat captura de ecran a raportului de eroare"</string> @@ -292,20 +292,20 @@ <string name="notification_channel_accessibility_security_policy" msgid="1727787021725251912">"Folosirea accesibilității"</string> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> folosește bateria"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicații folosesc bateria"</string> - <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Atingeți pentru mai multe detalii privind bateria și utilizarea datelor"</string> + <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Atinge pentru mai multe detalii privind bateria și utilizarea datelor"</string> <string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string> <string name="safeMode" msgid="8974401416068943888">"Mod sigur"</string> <string name="android_system_label" msgid="5974767339591067210">"Sistemul Android"</string> - <string name="user_owner_label" msgid="8628726904184471211">"Comutați la profilul personal"</string> - <string name="managed_profile_label" msgid="7316778766973512382">"Comutați la profilul de serviciu"</string> + <string name="user_owner_label" msgid="8628726904184471211">"Comută la profilul personal"</string> + <string name="managed_profile_label" msgid="7316778766973512382">"Comută la profilul de serviciu"</string> <string name="permgrouplab_contacts" msgid="4254143639307316920">"Agendă"</string> - <string name="permgroupdesc_contacts" msgid="9163927941244182567">"acceseze persoanele de contact"</string> + <string name="permgroupdesc_contacts" msgid="9163927941244182567">"să acceseze agenda"</string> <string name="permgrouplab_location" msgid="1858277002233964394">"Locație"</string> - <string name="permgroupdesc_location" msgid="1995955142118450685">"acceseze locația acestui dispozitiv"</string> + <string name="permgroupdesc_location" msgid="1995955142118450685">"să acceseze locația acestui dispozitiv"</string> <string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendar"</string> - <string name="permgroupdesc_calendar" msgid="6762751063361489379">"acceseze calendarul"</string> + <string name="permgroupdesc_calendar" msgid="6762751063361489379">"să acceseze calendarul"</string> <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string> - <string name="permgroupdesc_sms" msgid="5726462398070064542">"trimită și să vadă mesajele SMS"</string> + <string name="permgroupdesc_sms" msgid="5726462398070064542">"să trimită și să vadă mesajele SMS"</string> <string name="permgrouplab_storage" msgid="17339216290379241">"Fișiere"</string> <string name="permgroupdesc_storage" msgid="5378659041354582769">"să acceseze fișiere de pe dispozitiv"</string> <string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"Muzică și conținut audio"</string> @@ -315,32 +315,32 @@ <string name="permgrouplab_microphone" msgid="2480597427667420076">"Microfon"</string> <string name="permgroupdesc_microphone" msgid="1047786732792487722">"înregistreze sunet"</string> <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Activitate fizică"</string> - <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"accesați activitatea fizică"</string> + <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"să acceseze activitatea fizică"</string> <string name="permgrouplab_camera" msgid="9090413408963547706">"Camera foto"</string> <string name="permgroupdesc_camera" msgid="7585150538459320326">"fotografieze și să înregistreze videoclipuri"</string> <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Dispozitive din apropiere"</string> - <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"descoperiți dispozitive din apropiere și conectați-vă la acestea"</string> + <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"descoperă dispozitive din apropiere și conectează-te la acestea"</string> <string name="permgrouplab_calllog" msgid="7926834372073550288">"Jurnale de apeluri"</string> <string name="permgroupdesc_calllog" msgid="2026996642917801803">"să citească și să scrie jurnalul de apeluri telefonice"</string> <string name="permgrouplab_phone" msgid="570318944091926620">"Telefon"</string> - <string name="permgroupdesc_phone" msgid="270048070781478204">"inițieze și să gestioneze apeluri telefonice"</string> + <string name="permgroupdesc_phone" msgid="270048070781478204">"să inițieze și să gestioneze apeluri telefonice"</string> <string name="permgrouplab_sensors" msgid="9134046949784064495">"Senzori corporali"</string> - <string name="permgroupdesc_sensors" msgid="2610631290633747752">"acceseze datele de la senzori despre semnele vitale"</string> + <string name="permgroupdesc_sensors" msgid="2610631290633747752">"să acceseze datele de la senzori despre semnele vitale"</string> <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notificări"</string> <string name="permgroupdesc_notifications" msgid="4608679556801506580">"să afișeze notificări"</string> - <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Analizeze conținutul ferestrei"</string> - <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspectează conținutul unei ferestre cu care interacționați."</string> - <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activeze funcția Explorați prin atingere"</string> + <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"să preia conținutul ferestrei"</string> + <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspectează conținutul unei ferestre cu care interacționezi."</string> + <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"să activeze funcția Explorează prin atingere"</string> <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Elementele atinse vor fi rostite cu voce tare, iar ecranul poate fi explorat utilizând gesturi."</string> - <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Remarce textul pe care îl introduceți"</string> - <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Include date personale, cum ar fi numere ale cardurilor de credit sau parole."</string> + <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"să vadă textul pe care îl introduci"</string> + <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Include date cu caracter personal, cum ar fi numere ale cardurilor de credit sau parole."</string> <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Controlează mărirea pe afișaj"</string> <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controlează nivelul de zoom și poziționarea afișajului."</string> <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Folosește gesturi"</string> <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Poate atinge, glisa, ciupi sau folosi alte gesturi."</string> - <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Redea gesturi ce implică amprente"</string> + <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"să redea gesturi ce implică amprente"</string> <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Poate reda gesturile făcute pe senzorul de amprentă al dispozitivului."</string> - <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Faceți o captură de ecran"</string> + <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Fă o captură de ecran"</string> <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Poate face o captură de ecran."</string> <string name="permlab_statusBar" msgid="8798267849526214017">"dezactivare sau modificare bare de stare"</string> <string name="permdesc_statusBar" msgid="5809162768651019642">"Permite aplicației să dezactiveze bara de stare sau să adauge și să elimine pictograme de sistem."</string> @@ -359,34 +359,34 @@ <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"să răspundă la apeluri telefonice"</string> <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Permite aplicației să răspundă la un apel telefonic."</string> <string name="permlab_receiveSms" msgid="505961632050451881">"primește mesaje text (SMS)"</string> - <string name="permdesc_receiveSms" msgid="1797345626687832285">"Permite aplicației să primească și să proceseze mesaje SMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta."</string> + <string name="permdesc_receiveSms" msgid="1797345626687832285">"Permite aplicației să primească și să proceseze mesaje SMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitiv fără a ți le arăta."</string> <string name="permlab_receiveMms" msgid="4000650116674380275">"primește mesaje text (MMS)"</string> - <string name="permdesc_receiveMms" msgid="958102423732219710">"Permite aplicației să primească și să proceseze mesaje MMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta."</string> + <string name="permdesc_receiveMms" msgid="958102423732219710">"Permite aplicației să primească și să proceseze mesaje MMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitiv fără a ți le arăta."</string> <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"Redirecționează mesajele cu transmisie celulară"</string> - <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Permite aplicației să se conecteze la modulul de transmisie celulară pentru a redirecționa mesajele cu transmisie celulară pe măsură ce le primește. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situațiile de urgență. Aplicațiile rău intenționate pot afecta performanța sau funcționarea dispozitivului dvs. când este primită o transmisie celulară de urgență."</string> + <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Permite aplicației să se conecteze la modulul de transmisie celulară pentru a redirecționa mesajele cu transmisie celulară pe măsură ce le primește. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a te avertiza cu privire la situațiile de urgență. Aplicațiile rău intenționate pot afecta performanța sau funcționarea dispozitivului când e primită o transmisie celulară de urgență."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Să gestioneze apelurile în desfășurare"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Permite unei aplicații să vadă detalii despre apelurile în desfășurare de pe dispozitiv și să gestioneze apelurile respective."</string> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"citește mesajele cu transmisie celulară"</string> - <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitivul dvs. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situațiile de urgență. Aplicațiile rău intenționate pot afecta performanța sau funcționarea dispozitivului dvs. când este primită o transmisie celulară de urgență."</string> + <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitiv. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a te avertiza cu privire la situațiile de urgență. Aplicațiile rău intenționate pot afecta performanța sau funcționarea dispozitivului când e primită o transmisie celulară de urgență."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"citire feeduri abonat"</string> <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Permite aplicației să obțină detalii despre feedurile sincronizate în prezent."</string> - <string name="permlab_sendSms" msgid="7757368721742014252">"trimită și să vadă mesajele SMS"</string> - <string name="permdesc_sendSms" msgid="6757089798435130769">"Permite aplicației să trimită mesaje SMS, ceea ce ar putea determina apariția unor taxe neașteptate. Aplicațiile rău intenționate pot acumula costuri prin trimiterea mesajelor fără confirmarea dvs."</string> + <string name="permlab_sendSms" msgid="7757368721742014252">"să trimită și să vadă mesajele SMS"</string> + <string name="permdesc_sendSms" msgid="6757089798435130769">"Permite aplicației să trimită mesaje SMS, ceea ce ar putea duce la costuri neașteptate. Aplicațiile rău intenționate pot acumula costuri prin trimiterea mesajelor fără confirmarea ta."</string> <string name="permlab_readSms" msgid="5164176626258800297">"citește mesajele text (SMS sau MMS)"</string> <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"Această aplicație poate citi toate mesajele SMS stocate pe tabletă."</string> <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"Această aplicație poate să citească toate mesajele SMS (texT) stocate pe dispozitivul Android TV."</string> <string name="permdesc_readSms" product="default" msgid="774753371111699782">"Această aplicație poate citi toate mesajele SMS stocate pe telefon."</string> <string name="permlab_receiveWapPush" msgid="4223747702856929056">"primește mesaje text (WAP)"</string> - <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"Permite aplicației să primească și să proceseze mesaje WAP. Această permisiune include capacitatea de a monitoriza sau șterge mesajele care v-au fost trimise fără a vi le arăta."</string> + <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"Permite aplicației să primească și să proceseze mesaje WAP. Această permisiune include capacitatea de a monitoriza sau șterge mesajele care ți-au fost trimise fără a ți le arăta."</string> <string name="permlab_getTasks" msgid="7460048811831750262">"preluare aplicații care rulează"</string> <string name="permdesc_getTasks" msgid="7388138607018233726">"Permite aplicației să preia informațiile despre activitățile care rulează în prezent și care au rulat recent. În acest fel, aplicația poate descoperi informații despre aplicațiile care sunt utilizate pe dispozitiv."</string> <string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"să gestioneze profilul și proprietarii dispozitivului"</string> <string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"Permite aplicațiilor să seteze proprietarii de profiluri și proprietarul dispozitivului."</string> <string name="permlab_reorderTasks" msgid="7598562301992923804">"reordonare aplicații care rulează"</string> - <string name="permdesc_reorderTasks" msgid="8796089937352344183">"Permite aplicației să mute activitățile în prim-plan și în fundal. Aplicația poate face acest lucru fără aportul dvs."</string> + <string name="permdesc_reorderTasks" msgid="8796089937352344183">"Permite aplicației să mute activitățile în prim-plan și în fundal. Aplicația poate face acest lucru fără intervenția ta."</string> <string name="permlab_enableCarMode" msgid="893019409519325311">"activare mod Mașină"</string> <string name="permdesc_enableCarMode" msgid="56419168820473508">"Permite aplicației să activeze modul Mașină."</string> - <string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"închide alte aplicații"</string> + <string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"să închidă alte aplicații"</string> <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"Permite aplicației să oprească procesele derulate în fundal de alte aplicații. Acest lucru poate face ca respectivele aplicații să nu mai ruleze."</string> <string name="permlab_systemAlertWindow" msgid="5757218350944719065">"Această aplicație poate apărea deasupra altor aplicații"</string> <string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"Această aplicație poate apărea deasupra altor aplicații sau a altor părți ale ecranului. Acest lucru poate să afecteze utilizarea normală a aplicației și să schimbe modul în care se afișează alte aplicații."</string> @@ -403,7 +403,7 @@ <string name="permlab_getPackageSize" msgid="375391550792886641">"măsurare spațiu de stocare al aplicației"</string> <string name="permdesc_getPackageSize" msgid="742743530909966782">"Permite aplicației să preia dimensiunile codului, ale datelor și ale memoriei cache"</string> <string name="permlab_writeSettings" msgid="8057285063719277394">"modifică setări de sistem"</string> - <string name="permdesc_writeSettings" msgid="8293047411196067188">"Permite aplicației să modifice datele din setările sistemului. Aplicațiile rău intenționate pot corupe configurația sistemului dvs."</string> + <string name="permdesc_writeSettings" msgid="8293047411196067188">"Permite aplicației să modifice datele din setările sistemului. Aplicațiile rău intenționate pot corupe configurația sistemului."</string> <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"rulează la pornire"</string> <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"Permite aplicației să pornească imediat ce s-a terminat încărcarea sistemului. Din acest motiv, pornirea tabletei poate dura mai mult timp, iar rularea continuă a aplicației poate încetini dispozitivul."</string> <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"Permite aplicației să pornească imediat ce s-a terminat încărcarea sistemului. Din acest motiv, pornirea dispozitivului Android TV poate dura mai mult timp, iar rularea continuă a aplicației poate încetini dispozitivul."</string> @@ -413,9 +413,9 @@ <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"Permite aplicației să trimită mesaje difuzate persistente, care se păstrează după terminarea difuzării mesajului. Utilizarea excesivă a acestei funcții poate să încetinească sau să destabilizeze dispozitivul Android TV, determinându-l să utilizeze prea multă memorie."</string> <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"Permite aplicației să trimită mesaje difuzate persistente, care se păstrează după terminarea difuzării mesajului. Utilizarea excesivă a acestei funcții poate să încetinească sau să destabilizeze telefonul, determinându-l să utilizeze prea multă memorie."</string> <string name="permlab_readContacts" msgid="8776395111787429099">"citește agenda"</string> - <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"Permite aplicației să citească datele despre persoanele din agenda stocată pe tabletă. Aplicațiile vor avea și acces la conturile de pe tabletă care au creat agenda. Aici pot fi incluse conturile create de aplicațiile pe care le-ați instalat. Cu această permisiune, aplicațiile pot salva datele de contact, iar aplicațiile rău-intenționate pot permite accesul la datele de contact fără cunoștința dvs."</string> - <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"Permite aplicației să citească datele despre persoanele de contact din agenda stocată pe dispozitivul Android TV. Aplicațiile vor avea și acces la conturile de pe dispozitivul Android TV care au creat agenda. Aici pot fi incluse conturile create de aplicațiile pe care le-ați instalat. Cu această permisiune, aplicațiile pot salva datele de contact, iar aplicațiile rău-intenționate pot permite accesul la datele de contact fără cunoștința dvs."</string> - <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"Permite aplicației să citească datele despre persoanele de contact salvate pe telefon. Aplicațiile vor avea și acces la conturile de pe telefon care au creat agenda. Aici pot fi incluse conturile create de aplicațiile pe care le-ați instalat. Cu această permisiune, aplicațiile pot salva datele de contact, iar aplicațiile rău-intenționate pot permite accesul la datele de contact fără cunoștința dvs."</string> + <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"Permite aplicației să citească datele despre persoanele din agenda stocată pe tabletă. Aplicațiile vor avea și acces la conturile de pe tabletă care au creat agenda. Aici pot fi incluse conturile create de aplicațiile pe care le-ai instalat. Cu această permisiune, aplicațiile pot salva datele de contact, iar aplicațiile rău intenționate pot permite accesul la datele de contact fără cunoștința ta."</string> + <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"Permite aplicației să citească datele despre persoanele de contact din agenda stocată pe dispozitivul Android TV. Aplicațiile vor avea și acces la conturile de pe dispozitivul Android TV care au creat agenda. Aici pot fi incluse conturile create de aplicațiile pe care le-ai instalat. Cu această permisiune, aplicațiile pot salva datele de contact, iar aplicațiile rău intenționate pot permite accesul la datele de contact fără cunoștința ta."</string> + <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"Permite aplicației să citească datele despre persoanele de contact salvate pe telefon. Aplicațiile vor avea și acces la conturile de pe telefon care au creat agenda. Aici pot fi incluse conturile create de aplicațiile pe care le-ai instalat. Cu această permisiune, aplicațiile pot salva datele de contact, iar aplicațiile rău intenționate pot permite accesul la datele de contact fără cunoștința ta."</string> <string name="permlab_writeContacts" msgid="8919430536404830430">"modifică agenda"</string> <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe tabletă. Cu această permisiune, aplicația poate șterge datele de contact."</string> <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe dispozitivul Android TV. Cu această permisiune, aplicația poate șterge datele de contact."</string> @@ -423,9 +423,9 @@ <string name="permlab_readCallLog" msgid="1739990210293505948">"citește jurnalul de apeluri"</string> <string name="permdesc_readCallLog" msgid="8964770895425873433">"Această aplicație poate citi istoricul apelurilor."</string> <string name="permlab_writeCallLog" msgid="670292975137658895">"scrie jurnalul de apeluri"</string> - <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Permite aplicației să modifice jurnalul de apeluri al tabletei dvs., inclusiv datele despre apelurile primite sau efectuate. Aplicațiile rău intenționate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul dvs. de apeluri."</string> + <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Permite aplicației să modifice jurnalul de apeluri al tabletei, inclusiv datele despre apelurile primite sau făcute. Aplicațiile rău intenționate pot folosi această permisiune pentru a șterge sau a modifica jurnalul de apeluri."</string> <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Permite aplicației să modifice jurnalul de apeluri al dispozitivului Android TV, inclusiv datele despre apelurile primite sau efectuate. Aplicațiile rău intenționate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul de apeluri."</string> - <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Permite aplicației să modifice jurnalul de apeluri al telefonului dvs., inclusiv datele despre apelurile primite sau efectuate. Aplicațiile rău intenționate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul dvs. de apeluri."</string> + <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Permite aplicației să modifice jurnalul de apeluri al telefonului, inclusiv datele despre apelurile primite sau făcute. Aplicațiile rău intenționate pot folosi această permisiune pentru a șterge sau a modifica jurnalul de apeluri."</string> <string name="permlab_bodySensors" msgid="662918578601619569">"Să acceseze date de la senzorii corporali, cum ar fi pulsul, în timpul folosirii"</string> <string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"Permite aplicației să acceseze date de la senzorii corporali, cum ar fi pulsul, temperatura și procentul de oxigen din sânge, în timpul folosirii aplicației."</string> <string name="permlab_bodySensors_background" msgid="4912560779957760446">"Să acceseze date de la senzorii corporali, precum pulsul, când rulează în fundal"</string> @@ -441,43 +441,43 @@ <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"accesare comenzi suplimentare ale furnizorului locației"</string> <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Permite aplicației să acceseze comenzi suplimentare pentru furnizorul locației. Aplicația ar putea să utilizeze această permisiune pentru a influența operațiile GPS sau ale altor surse de locații."</string> <string name="permlab_accessFineLocation" msgid="6426318438195622966">"să acceseze locația exactă în prim-plan"</string> - <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Aplicația vă poate determina locația exactă cu ajutorul serviciilor de localizare atunci când este folosită. Pentru ca aplicația să poată determina locația, trebuie să activați serviciile de localizare pentru dispozitiv. Aceasta poate mări utilizarea bateriei."</string> + <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Aplicația îți poate stabili locația exactă cu ajutorul serviciilor de localizare când este folosită. Pentru ca aplicația să poată stabili locația, trebuie să activezi serviciile de localizare pentru dispozitiv. Aceasta poate mări utilizarea bateriei."</string> <string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"să acceseze locația aproximativă numai în prim-plan."</string> - <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Aplicația vă poate determina locația aproximativă cu ajutorul serviciilor de localizare atunci când este folosită. Pentru ca aplicația să poată determina locația, trebuie să activați serviciile de localizare pentru dispozitiv."</string> - <string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"accesați locația în fundal"</string> + <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Aplicația îți poate stabili locația aproximativă cu ajutorul serviciilor de localizare când este folosită. Pentru ca aplicația să poată stabili locația, trebuie să activezi serviciile de localizare pentru dispozitiv."</string> + <string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"să acceseze locația în fundal"</string> <string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Aplicația poate accesa locația oricând, chiar dacă nu este folosită."</string> <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"modificare setări audio"</string> <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Permite aplicației să modifice setările audio globale, cum ar fi volumul și difuzorul care este utilizat pentru ieșire."</string> - <string name="permlab_recordAudio" msgid="1208457423054219147">"înregistreze sunet"</string> + <string name="permlab_recordAudio" msgid="1208457423054219147">"să înregistreze sunet"</string> <string name="permdesc_recordAudio" msgid="5857246765327514062">"Această aplicație poate să înregistreze conținut audio folosind microfonul când este în uz."</string> <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"să înregistreze conținut audio în fundal"</string> <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Această aplicație poate înregistra conținut audio folosind microfonul oricând."</string> <string name="permlab_sim_communication" msgid="176788115994050692">"să trimită comenzi către SIM"</string> <string name="permdesc_sim_communication" msgid="4179799296415957960">"Permite aplicației să trimită comenzi pe cardul SIM. Această permisiune este foarte periculoasă."</string> <string name="permlab_activityRecognition" msgid="1782303296053990884">"recunoașterea activității fizice"</string> - <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Această aplicație vă poate recunoaște activitatea fizică."</string> + <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Această aplicație îți poate recunoaște activitatea fizică."</string> <string name="permlab_camera" msgid="6320282492904119413">"realizarea de fotografii și videoclipuri"</string> <string name="permdesc_camera" msgid="5240801376168647151">"Această aplicație poate să fotografieze și să înregistreze videoclipuri folosind camera foto când este în uz."</string> <string name="permlab_backgroundCamera" msgid="7549917926079731681">"să fotografieze și să înregistreze videoclipuri în fundal"</string> <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Această aplicație poate să fotografieze și să înregistreze videoclipuri folosind camera foto oricând."</string> - <string name="permlab_systemCamera" msgid="3642917457796210580">"Permiteți unei aplicații sau unui serviciu accesul la camerele de sistem, ca să fotografieze și să înregistreze videoclipuri"</string> + <string name="permlab_systemCamera" msgid="3642917457796210580">"Permite unei aplicații sau unui serviciu accesul la camerele de sistem, ca să fotografieze și să înregistreze videoclipuri"</string> <string name="permdesc_systemCamera" msgid="5938360914419175986">"Această aplicație de sistem privilegiată poate să fotografieze și să înregistreze videoclipuri folosind o cameră de sistem în orice moment. Necesită și permisiunea android.permission.CAMERA pentru aplicație"</string> - <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permiteți unei aplicații sau unui serviciu să primească apeluri inverse atunci când sunt deschise sau închise dispozitive cu cameră."</string> + <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permite unei aplicații sau unui serviciu să primească apeluri inverse atunci când sunt deschise sau închise dispozitive cu cameră."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Această aplicație poate primi apeluri inverse atunci când este deschis (de aplicație) sau închis orice dispozitiv cu cameră."</string> <string name="permlab_vibrate" msgid="8596800035791962017">"controlează vibrarea"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Permite aplicației să controleze mecanismul de vibrare."</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Permite aplicației să acceseze modul de vibrații."</string> - <string name="permlab_callPhone" msgid="1798582257194643320">"apelare directă numere de telefon"</string> - <string name="permdesc_callPhone" msgid="5439809516131609109">"Permite aplicației să apeleze numere de telefon fără intervenția dvs. Acest lucru poate determina apariția unor taxe sau a unor apeluri neașteptate. Cu această permisiune aplicația nu poate apela numerele de urgență. Aplicațiile rău intenționate pot acumula costuri prin efectuarea unor apeluri fără confirmare."</string> + <string name="permlab_callPhone" msgid="1798582257194643320">"să sune direct la numere de telefon"</string> + <string name="permdesc_callPhone" msgid="5439809516131609109">"Permite aplicației să apeleze numere de telefon fără intervenția ta. Acest lucru poate determina apariția unor taxe sau a unor apeluri neașteptate. Cu această permisiune aplicația nu poate apela numerele de urgență. Aplicațiile rău intenționate pot acumula costuri prin efectuarea unor apeluri fără confirmare."</string> <string name="permlab_accessImsCallService" msgid="442192920714863782">"accesează serviciul de apelare IMS"</string> - <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite aplicației să folosească serviciul IMS pentru apeluri, fără intervenția dvs."</string> + <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite aplicației să folosească serviciul IMS pentru apeluri, fără intervenția ta."</string> <string name="permlab_readPhoneState" msgid="8138526903259297969">"citește starea și identitatea telefonului"</string> <string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite aplicației să acceseze funcțiile de telefon ale dispozitivului. Cu această permisiune aplicația stabilește numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanță conectat printr-un apel."</string> <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"să citească informații de bază, precum activitatea și starea telefonului"</string> <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permite ca aplicația să acceseze funcțiile de telefonie de bază ale dispozitivului."</string> <string name="permlab_manageOwnCalls" msgid="9033349060307561370">"să direcționeze apelurile prin intermediul sistemului"</string> - <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permiteți aplicației să direcționeze apelurile prin intermediul sistemului pentru a îmbunătăți calitatea apelurilor."</string> - <string name="permlab_callCompanionApp" msgid="3654373653014126884">"Vedeți și controlați apelurile prin intermediul sistemului."</string> + <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permite aplicației să direcționeze apelurile prin intermediul sistemului pentru a îmbunătăți calitatea apelurilor."</string> + <string name="permlab_callCompanionApp" msgid="3654373653014126884">"Vezi și controlează apelurile prin intermediul sistemului."</string> <string name="permdesc_callCompanionApp" msgid="8474168926184156261">"Permite aplicației să vadă și să controleze apelurile în desfășurare pe dispozitiv. Aceasta include informații ca numerele pentru apeluri și starea apelurilor."</string> <string name="permlab_exemptFromAudioRecordRestrictions" msgid="1164725468350759486">"scutită de restricțiile pentru înregistrarea conținutului audio"</string> <string name="permdesc_exemptFromAudioRecordRestrictions" msgid="2425117015896871976">"Scutiți aplicația de restricțiile pentru înregistrarea conținutului audio."</string> @@ -506,10 +506,10 @@ <string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"Permite aplicației să schimbe fusul orar al dispozitivului Android TV."</string> <string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"Permite aplicației să schimbe fusul orar al telefonului."</string> <string name="permlab_getAccounts" msgid="5304317160463582791">"găsește conturi pe dispozitiv"</string> - <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"Permite aplicației să obțină lista de conturi cunoscute de tabletă. Aceasta poate include conturile create de aplicațiile pe care le-ați instalat."</string> - <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"Permite aplicației să obțină lista conturilor cunoscute de dispozitivul Android TV. Aceasta poate include conturile create de aplicațiile pe care le-ați instalat."</string> - <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"Permite aplicației să obțină lista de conturi cunoscute de telefon. Aceasta poate include conturile create de aplicațiile pe care le-ați instalat."</string> - <string name="permlab_accessNetworkState" msgid="2349126720783633918">"vizualizează conexiunile la rețea"</string> + <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"Permite aplicației să obțină lista de conturi cunoscute de tabletă. Aceasta poate include conturile create de aplicațiile pe care le-ai instalat."</string> + <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"Permite aplicației să obțină lista conturilor cunoscute de dispozitivul Android TV. Aceasta poate include conturile create de aplicațiile pe care le-ai instalat."</string> + <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"Permite aplicației să obțină lista de conturi cunoscute de telefon. Aceasta poate include conturile create de aplicațiile pe care le-ai instalat."</string> + <string name="permlab_accessNetworkState" msgid="2349126720783633918">"să vadă conexiunile la rețea"</string> <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"Permite aplicației să vadă informațiile despre conexiunile la rețea, cum ar fi rețelele existente și cele care sunt conectate."</string> <string name="permlab_createNetworkSockets" msgid="3224420491603590541">"să aibă acces deplin la rețea"</string> <string name="permdesc_createNetworkSockets" msgid="7722020828749535988">"Permite aplicației să creeze socluri de rețea și să utilizeze protocoale de rețea personalizate. Browserul și alte aplicații oferă mijloacele de trimitere a datelor pe internet, astfel încât această permisiune nu este necesară pentru trimiterea datelor pe internet."</string> @@ -517,28 +517,28 @@ <string name="permdesc_changeNetworkState" msgid="649341947816898736">"Permite aplicației să modifice starea de conectivitate la rețea."</string> <string name="permlab_changeTetherState" msgid="9079611809931863861">"modificare conectivitate tethering"</string> <string name="permdesc_changeTetherState" msgid="3025129606422533085">"Permite aplicației să modifice starea de conectivitate prin tethering la rețea."</string> - <string name="permlab_accessWifiState" msgid="5552488500317911052">"vizualizează conexiunile Wi-Fi"</string> + <string name="permlab_accessWifiState" msgid="5552488500317911052">"să vadă conexiunile Wi-Fi"</string> <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Permite aplicației să vadă informațiile despre rețelele Wi-Fi, de ex. dacă o rețea Wi-Fi este activată, precum și numele dispozitivelor conectate la rețeaua Wi-Fi."</string> <string name="permlab_changeWifiState" msgid="7947824109713181554">"se conectează și se deconectează de la Wi-Fi"</string> <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Permite aplicației să se conecteze și să se deconecteze de la punctele de acces Wi-Fi, precum și să efectueze modificări în configurația dispozitivului pentru rețelele Wi-Fi."</string> <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"permitere recepționare difuzare multiplă Wi-Fi"</string> - <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Permite aplicației să primească pachetele trimise către toate dispozitivele dintr-o rețea Wi-Fi, utilizând adrese cu difuzare multiplă, nu doar tableta dvs. Această funcție utilizează mai multă energie decât modul fără difuzare multiplă."</string> + <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Permite aplicației să primească pachetele trimise către toate dispozitivele dintr-o rețea Wi-Fi, folosind adrese cu difuzare multiplă, nu doar tableta ta. Această funcție folosește mai multă energie decât modul fără difuzare multiplă."</string> <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Permite aplicației să primească pachetele trimise către toate dispozitivele dintr-o rețea Wi-Fi, utilizând adrese cu difuzare multiplă, nu doar dispozitivul Android TV. Această funcție utilizează mai multă energie decât modul fără difuzare multiplă."</string> - <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Permite aplicației să primească pachetele trimise către toate dispozitivele dintr-o rețea Wi-Fi, utilizând adrese cu difuzare multiplă, nu doar telefonul dvs. Această funcție utilizează mai multă energie decât modul fără difuzare multiplă."</string> + <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Permite aplicației să primească pachetele trimise către toate dispozitivele dintr-o rețea Wi-Fi, folosind adrese cu difuzare multiplă, nu doar telefonul tău. Această funcție folosește mai multă energie decât modul fără difuzare multiplă."</string> <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"accesează setările Bluetooth"</string> - <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Permite aplicației să configureze tableta Bluetooth locală, să descopere și să se împerecheze cu dispozitive la distanță."</string> - <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Permite aplicației să configureze conexiunea Bluetooth pe dispozitivul Android TV, să descopere și să se împerecheze cu dispozitive la distanță."</string> - <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Permite aplicației să configureze telefonul Bluetooth local, să descopere și să se împerecheze cu dispozitive la distanță."</string> + <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Permite aplicației să configureze tableta Bluetooth locală, să descopere și să se asocieze cu dispozitive la distanță."</string> + <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Permite aplicației să configureze conexiunea Bluetooth pe dispozitivul Android TV, să descopere și să se asocieze cu dispozitive la distanță."</string> + <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Permite aplicației să configureze telefonul Bluetooth local, să descopere și să se asocieze cu dispozitive la distanță."</string> <string name="permlab_accessWimaxState" msgid="7029563339012437434">"se conectează și se deconectează de la WiMAX"</string> <string name="permdesc_accessWimaxState" msgid="5372734776802067708">"Permite aplicației să stabilească dacă o rețea WiMAX este activată și să vadă informațiile cu privire la toate rețelele WiMAX conectate."</string> - <string name="permlab_changeWimaxState" msgid="6223305780806267462">"schimbați starea WiMAX"</string> + <string name="permlab_changeWimaxState" msgid="6223305780806267462">"schimbă starea WiMAX"</string> <string name="permdesc_changeWimaxState" product="tablet" msgid="4011097664859480108">"Permite aplicației să conecteze și să deconecteze tableta la și de la rețelele WiMAX."</string> <string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"Permite aplicației să conecteze și să deconecteze dispozitivul Android TV de la rețelele WiMAX."</string> <string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"Permite aplicației să conecteze și să deconecteze telefonul la și de la rețelele WiMAX."</string> <string name="permlab_bluetooth" msgid="586333280736937209">"conectează dispozitive Bluetooth"</string> - <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Permite aplicației să vadă configurația tabletei Bluetooth, să efectueze și să accepte conexiuni cu dispozitive împerecheate."</string> - <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Permite aplicației să vadă configurația conexiunii prin Bluetooth a dispozitivului Android TV, să efectueze și să accepte conexiuni cu dispozitive împerecheate."</string> - <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Permite aplicației să vadă configurația telefonului Bluetooth, să efectueze și să accepte conexiuni cu dispozitive împerecheate."</string> + <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Permite aplicației să vadă configurația tabletei Bluetooth, să facă și să accepte conexiuni cu dispozitive asociate."</string> + <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Permite aplicației să vadă configurația conexiunii prin Bluetooth a dispozitivului Android TV, să efectueze și să accepte conexiuni cu dispozitive asociate."</string> + <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Permite aplicației să vadă configurația telefonului Bluetooth, să stabilească și să accepte conexiuni cu dispozitive asociate."</string> <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"să descopere și să asocieze dispozitive Bluetooth din apropiere"</string> <string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Permite aplicației să descopere și să asocieze dispozitive Bluetooth din apropiere"</string> <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"să se conecteze la dispozitive Bluetooth asociate"</string> @@ -546,7 +546,7 @@ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"să transmită anunțuri pe dispozitive Bluetooth din apropiere"</string> <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite aplicației să difuzeze anunțuri pe dispozitive Bluetooth din apropiere"</string> <string name="permlab_uwb_ranging" msgid="8141915781475770665">"să stabilească poziția relativă dintre dispozitivele Ultra-Wideband din apropiere"</string> - <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permiteți-i aplicației să stabilească poziția relativă dintre dispozitivele Ultra-Wideband din apropiere"</string> + <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite-i aplicației să stabilească poziția relativă dintre dispozitivele Ultra-Wideband din apropiere"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"să interacționeze cu dispozitive Wi‑Fi din apropiere"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite aplicației să se conecteze la dispozitive Wi-Fi din apropiere, să transmită anunțuri și să stabilească poziția relativă a acestora"</string> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informații despre serviciul de plăți NFC preferat"</string> @@ -555,55 +555,55 @@ <string name="permdesc_nfc" msgid="8352737680695296741">"Permite aplicației să comunice cu etichetele, cardurile și cititoarele NFC (Near Field Communication)."</string> <string name="permlab_disableKeyguard" msgid="3605253559020928505">"dezactivează blocarea ecranului"</string> <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Permite aplicației să dezactiveze blocarea tastelor și orice modalitate asociată de securizare prin parolă. De exemplu, telefonul dezactivează blocarea tastelor când se primește un apel telefonic și reactivează blocarea tastelor la terminarea apelului."</string> - <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"solicitați complexitatea blocării ecranului"</string> - <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Permite aplicației să învețe nivelul de complexitate al blocării ecranului (ridicat, mediu, scăzut sau fără), fapt ce indică intervalul posibil de lungime a parolei și tipul de blocare a ecranului. Aplicația le poate sugera utilizatorilor să își actualizeze blocarea ecranului la un anumit nivel, dar utilizatorii pot ignora sugestia și pot naviga în continuare. Rețineți că blocarea ecranului nu este stocată ca text simplu, astfel încât aplicația să nu cunoască parola exactă."</string> + <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"să solicite complexitatea blocării ecranului"</string> + <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Permite aplicației să învețe nivelul de complexitate al blocării ecranului (ridicat, mediu, scăzut sau fără), fapt ce indică intervalul posibil de lungime a parolei și tipul de blocare a ecranului. Aplicația le poate sugera utilizatorilor să își actualizeze blocarea ecranului la un anumit nivel, dar utilizatorii pot ignora sugestia și pot naviga în continuare. Reține că blocarea ecranului nu e stocată ca text simplu, astfel încât aplicația să nu cunoască parola exactă."</string> <string name="permlab_postNotification" msgid="4875401198597803658">"să afișeze notificări"</string> <string name="permdesc_postNotification" msgid="5974977162462877075">"Permite aplicației să afișeze notificări"</string> - <string name="permlab_useBiometric" msgid="6314741124749633786">"utilizați hardware biometric"</string> + <string name="permlab_useBiometric" msgid="6314741124749633786">"să folosească hardware biometric"</string> <string name="permdesc_useBiometric" msgid="7502858732677143410">"Permite aplicației să folosească hardware biometric pentru autentificare"</string> <string name="permlab_manageFingerprint" msgid="7432667156322821178">"gestionează hardware-ul pentru amprentă"</string> <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Permite aplicației să invoce metode pentru a adăuga și pentru a șterge șabloane de amprentă pentru utilizare."</string> <string name="permlab_useFingerprint" msgid="1001421069766751922">"folosește hardware-ul pentru amprentă"</string> <string name="permdesc_useFingerprint" msgid="412463055059323742">"Permite aplicației să folosească hardware pentru amprentă pentru autentificare"</string> - <string name="permlab_audioWrite" msgid="8501705294265669405">"modificați colecția de muzică"</string> - <string name="permdesc_audioWrite" msgid="8057399517013412431">"Permite aplicației să vă modifice colecția de muzică."</string> - <string name="permlab_videoWrite" msgid="5940738769586451318">"modificați colecția de videoclipuri"</string> - <string name="permdesc_videoWrite" msgid="6124731210613317051">"Permite aplicației să vă modifice colecția de videoclipuri."</string> - <string name="permlab_imagesWrite" msgid="1774555086984985578">"modificați colecția de fotografii"</string> - <string name="permdesc_imagesWrite" msgid="5195054463269193317">"Permite aplicației să vă modifice colecția de fotografii."</string> - <string name="permlab_mediaLocation" msgid="7368098373378598066">"citiți locațiile din colecția media"</string> - <string name="permdesc_mediaLocation" msgid="597912899423578138">"Permite aplicației să citească locațiile din colecția dvs. media."</string> - <string name="biometric_app_setting_name" msgid="3339209978734534457">"Folosiți sistemele biometrice"</string> - <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Folosiți sistemele biometrice sau blocarea ecranului"</string> - <string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirmați-vă identitatea"</string> - <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Folosiți sistemele biometrice pentru a continua"</string> - <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Folosiți sistemele biometrice sau blocarea ecranului pentru a continua"</string> + <string name="permlab_audioWrite" msgid="8501705294265669405">"să modifice colecția de muzică"</string> + <string name="permdesc_audioWrite" msgid="8057399517013412431">"Permite aplicației să modifice colecția de muzică."</string> + <string name="permlab_videoWrite" msgid="5940738769586451318">"să modifice colecția de videoclipuri"</string> + <string name="permdesc_videoWrite" msgid="6124731210613317051">"Permite aplicației să-ți modifice colecția de videoclipuri."</string> + <string name="permlab_imagesWrite" msgid="1774555086984985578">"să modifice colecția de fotografii"</string> + <string name="permdesc_imagesWrite" msgid="5195054463269193317">"Permite aplicației să-ți modifice colecția de fotografii."</string> + <string name="permlab_mediaLocation" msgid="7368098373378598066">"să citească locațiile din colecția media"</string> + <string name="permdesc_mediaLocation" msgid="597912899423578138">"Permite aplicației să citească locațiile din colecția ta media."</string> + <string name="biometric_app_setting_name" msgid="3339209978734534457">"Folosește sistemele biometrice"</string> + <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Folosește sistemele biometrice sau blocarea ecranului"</string> + <string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirmă-ți identitatea"</string> + <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Folosește sistemele biometrice pentru a continua"</string> + <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Folosește sistemele biometrice sau blocarea ecranului pentru a continua"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometric indisponibil"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentificarea a fost anulată"</string> <string name="biometric_not_recognized" msgid="5106687642694635888">"Nu este recunoscut"</string> <string name="biometric_error_canceled" msgid="8266582404844179778">"Autentificarea a fost anulată"</string> - <string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nu este setat niciun cod PIN, model sau parolă"</string> + <string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nu este setat un cod PIN, un model sau o parolă"</string> <string name="biometric_error_generic" msgid="6784371929985434439">"Eroare la autentificare"</string> - <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Folosiți blocarea ecranului"</string> - <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Introduceți blocarea ecranului ca să continuați"</string> - <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Apăsați ferm pe senzor"</string> + <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Folosește blocarea ecranului"</string> + <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Introdu blocarea ecranului pentru a continua"</string> + <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Apasă ferm pe senzor"</string> <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Amprenta nu a fost recunoscută. Încearcă din nou."</string> - <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Curățați senzorul de amprentă și încercați din nou"</string> - <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Curățați senzorul și încercați din nou"</string> - <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Apăsați ferm pe senzor"</string> - <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Ați mișcat degetul prea lent. Încercați din nou."</string> - <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Încercați altă amprentă"</string> + <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Curăță senzorul de amprentă și încearcă din nou"</string> + <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Curăță senzorul și încearcă din nou"</string> + <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Apasă ferm pe senzor"</string> + <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Ai mișcat degetul prea lent. Încearcă din nou."</string> + <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Încearcă altă amprentă"</string> <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Prea luminos"</string> <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"S-a detectat apăsarea butonului de alimentare"</string> - <string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Încercați să ajustați"</string> - <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Schimbați ușor poziția degetului de fiecare dată"</string> + <string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Încearcă să ajustezi"</string> + <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Schimbă ușor poziția degetului de fiecare dată"</string> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Amprenta nu a fost recunoscută"</string> <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Amprenta nu a fost recunoscută"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Amprentă autentificată"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Chip autentificat"</string> - <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Chip autentificat, apăsați Confirmați"</string> + <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Chip autentificat, apasă pe Confirmă"</string> <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware-ul pentru amprentă nu este disponibil."</string> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nu se poate configura amprenta"</string> <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Configurarea amprentei a expirat. Încearcă din nou."</string> @@ -615,79 +615,79 @@ <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nu au fost înregistrate amprente."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dispozitivul nu are senzor de amprentă."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzorul este dezactivat temporar."</string> - <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Nu se poate folosi senzorul de amprentă. Vizitați un furnizor de servicii de reparații."</string> + <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Nu se poate folosi senzorul de amprentă. Vizitează un furnizor de servicii de reparații."</string> <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"A fost apăsat butonul de pornire"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Degetul <xliff:g id="FINGERID">%d</xliff:g>"</string> - <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Folosiți amprenta"</string> - <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Folosiți amprenta sau blocarea ecranului"</string> - <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Folosiți amprenta pentru a continua"</string> - <string name="fingerprint_or_screen_lock_dialog_default_subtitle" msgid="5195808203117992200">"Folosiți amprenta sau blocarea ecranului pentru a continua"</string> + <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Folosește amprenta"</string> + <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Folosește amprenta sau blocarea ecranului"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Folosește amprenta pentru a continua"</string> + <string name="fingerprint_or_screen_lock_dialog_default_subtitle" msgid="5195808203117992200">"Folosește amprenta sau blocarea ecranului pentru a continua"</string> <string-array name="fingerprint_error_vendor"> </string-array> - <string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"A apărut o eroare. Încercați din nou."</string> + <string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"A apărut o eroare. Încearcă din nou."</string> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Pictograma amprentă"</string> <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Deblocare facială"</string> <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problemă cu Deblocarea facială"</string> - <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Atingeți pentru a șterge modelul facial, apoi adăugați din nou fața"</string> + <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Atinge pentru a șterge modelul facial, apoi adaugă din nou chipul"</string> <string name="face_setup_notification_title" msgid="8843461561970741790">"Configurează Deblocarea facială"</string> - <string name="face_setup_notification_content" msgid="5463999831057751676">"Deblocați-vă telefonul uitându-vă la acesta"</string> - <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pentru a folosi Deblocarea facială, activați "<b>"Accesul la cameră"</b>" în Setări și confidențialitate"</string> + <string name="face_setup_notification_content" msgid="5463999831057751676">"Deblochează-ți telefonul uitându-te la el"</string> + <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pentru a folosi Deblocarea facială, activează "<b>"Accesul la cameră"</b>" în Setări și confidențialitate"</string> <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurează mai multe moduri de deblocare"</string> - <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Atingeți ca să adăugați o amprentă"</string> + <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Atinge ca să adaugi o amprentă"</string> <string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Deblocare cu amprenta"</string> <string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Nu se poate folosi senzorul de amprentă"</string> - <string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Vizitați un furnizor de servicii de reparații."</string> - <string name="face_acquired_insufficient" msgid="6889245852748492218">"Nu se poate crea modelul facial. Reîncercați."</string> - <string name="face_acquired_too_bright" msgid="8070756048978079164">"Prea luminos. Încercați o lumină mai slabă."</string> + <string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Vizitează un furnizor de servicii de reparații."</string> + <string name="face_acquired_insufficient" msgid="6889245852748492218">"Nu se poate crea modelul facial. Reîncearcă."</string> + <string name="face_acquired_too_bright" msgid="8070756048978079164">"Prea luminos. Încearcă o lumină mai slabă."</string> <string name="face_acquired_too_dark" msgid="8539853432479385326">"Lumină insuficientă"</string> - <string name="face_acquired_too_close" msgid="4453646176196302462">"Mutați telefonul mai departe"</string> - <string name="face_acquired_too_far" msgid="2922278214231064859">"Mutați telefonul mai aproape"</string> - <string name="face_acquired_too_high" msgid="8278815780046368576">"Mutați telefonul mai sus"</string> - <string name="face_acquired_too_low" msgid="4075391872960840081">"Mutați telefonul mai jos"</string> - <string name="face_acquired_too_right" msgid="6245286514593540859">"Mutați telefonul spre stânga"</string> - <string name="face_acquired_too_left" msgid="9201762240918405486">"Mutați telefonul spre dreapta"</string> - <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Priviți mai direct spre dispozitiv."</string> - <string name="face_acquired_not_detected" msgid="1057966913397548150">"Nu vi se vede fața. Țineți telefonul la nivelul ochilor."</string> - <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Prea multă mișcare. Țineți telefonul nemișcat."</string> - <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Reînregistrați-vă chipul."</string> - <string name="face_acquired_too_different" msgid="2520389515612972889">"Chipul nu a fost recunoscut. Reîncercați."</string> - <string name="face_acquired_too_similar" msgid="8882920552674125694">"Schimbați ușor poziția capului"</string> + <string name="face_acquired_too_close" msgid="4453646176196302462">"Mută telefonul mai departe"</string> + <string name="face_acquired_too_far" msgid="2922278214231064859">"Mută telefonul mai aproape"</string> + <string name="face_acquired_too_high" msgid="8278815780046368576">"Mută telefonul mai sus"</string> + <string name="face_acquired_too_low" msgid="4075391872960840081">"Mută telefonul mai jos"</string> + <string name="face_acquired_too_right" msgid="6245286514593540859">"Mută telefonul spre stânga"</string> + <string name="face_acquired_too_left" msgid="9201762240918405486">"Mută telefonul spre dreapta"</string> + <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Privește mai direct spre dispozitiv."</string> + <string name="face_acquired_not_detected" msgid="1057966913397548150">"Nu ți se vede fața. Ține telefonul la nivelul ochilor."</string> + <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Prea multă mișcare. Ține telefonul nemișcat."</string> + <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Reînregistrează-ți chipul."</string> + <string name="face_acquired_too_different" msgid="2520389515612972889">"Chipul nu a fost recunoscut. Reîncearcă."</string> + <string name="face_acquired_too_similar" msgid="8882920552674125694">"Schimbă ușor poziția capului"</string> <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"Priviți direct spre telefon"</string> - <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"Priviți direct spre telefon"</string> - <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"Priviți direct spre telefon"</string> - <string name="face_acquired_obscured" msgid="4917643294953326639">"Eliminați orice vă ascunde chipul."</string> - <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Curățați partea de sus a ecranului, inclusiv bara neagră"</string> + <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"Privește mai direct spre telefon"</string> + <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"Privește mai direct spre telefon"</string> + <string name="face_acquired_obscured" msgid="4917643294953326639">"Îndepărtează orice îți ascunde chipul."</string> + <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Curăță partea de sus a ecranului, inclusiv bara neagră"</string> <!-- no translation found for face_acquired_dark_glasses_detected (5643703296620631986) --> <skip /> <!-- no translation found for face_acquired_mouth_covering_detected (8219428572168642593) --> <skip /> - <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Nu se poate crea modelul facial. Reîncercați."</string> + <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Nu se poate crea modelul facial. Reîncearcă."</string> <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"S-au detectat ochelari de culoare închisă. Chipul trebuie să fie vizibil în totalitate."</string> <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"S-a detectat un articol care acoperă chipul. Chipul trebuie să fie vizibil în totalitate."</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"Nu se poate confirma fața. Hardware-ul nu este disponibil."</string> - <string name="face_error_timeout" msgid="2598544068593889762">"Încercați din nou Deblocarea facială"</string> - <string name="face_error_no_space" msgid="5649264057026021723">"Nu se pot stoca date faciale noi. Ștergeți întâi unele vechi."</string> + <string name="face_error_timeout" msgid="2598544068593889762">"Încearcă din nou Deblocarea facială"</string> + <string name="face_error_no_space" msgid="5649264057026021723">"Nu se pot stoca date faciale noi. Șterge întâi unele vechi."</string> <string name="face_error_canceled" msgid="2164434737103802131">"Operațiunea privind chipul a fost anulată."</string> <string name="face_error_user_canceled" msgid="5766472033202928373">"Deblocarea facială a fost anulată de utilizator"</string> - <string name="face_error_lockout" msgid="7864408714994529437">"Prea multe încercări. Reîncercați mai târziu."</string> + <string name="face_error_lockout" msgid="7864408714994529437">"Prea multe încercări. Reîncearcă mai târziu."</string> <string name="face_error_lockout_permanent" msgid="3277134834042995260">"Prea multe încercări. Deblocarea facială este dezactivată."</string> - <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"Prea multe încercări. Folosiți blocarea ecranului."</string> - <string name="face_error_unable_to_process" msgid="5723292697366130070">"Nu se poate confirma fața. Încercați din nou."</string> - <string name="face_error_not_enrolled" msgid="1134739108536328412">"Nu ați configurat Deblocarea facială"</string> + <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"Prea multe încercări. Folosește blocarea ecranului."</string> + <string name="face_error_unable_to_process" msgid="5723292697366130070">"Nu se poate confirma fața. Încearcă din nou."</string> + <string name="face_error_not_enrolled" msgid="1134739108536328412">"Nu ai configurat Deblocarea facială"</string> <string name="face_error_hw_not_present" msgid="7940978724978763011">"Deblocarea facială nu este acceptată pe acest dispozitiv"</string> <string name="face_error_security_update_required" msgid="5076017208528750161">"Senzorul este dezactivat temporar."</string> <string name="face_name_template" msgid="3877037340223318119">"Chip <xliff:g id="FACEID">%d</xliff:g>"</string> - <string name="face_app_setting_name" msgid="5854024256907828015">"Folosiți Deblocarea facială"</string> - <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Folosiți deblocarea facială sau ecranul de blocare"</string> - <string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Folosiți-vă chipul ca să continuați"</string> - <string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Folosiți-vă chipul sau blocarea ecranului pentru a continua"</string> + <string name="face_app_setting_name" msgid="5854024256907828015">"Folosește Deblocarea facială"</string> + <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Folosește deblocarea facială sau ecranul de blocare"</string> + <string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Folosește-ți chipul pentru a continua"</string> + <string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Folosește-ți chipul sau blocarea ecranului pentru a continua"</string> <string-array name="face_error_vendor"> </string-array> - <string name="face_error_vendor_unknown" msgid="7387005932083302070">"A apărut o eroare. Încercați din nou."</string> + <string name="face_error_vendor_unknown" msgid="7387005932083302070">"A apărut o eroare. Încearcă din nou."</string> <string name="face_icon_content_description" msgid="465030547475916280">"Pictograma chip"</string> - <string name="permlab_readSyncSettings" msgid="6250532864893156277">"citire setări sincronizare"</string> + <string name="permlab_readSyncSettings" msgid="6250532864893156277">"să citească setări sincronizare"</string> <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Permite aplicației să citească setările de sincronizare ale unui cont. De exemplu, cu această permisiune aplicația poate determina dacă aplicația Persoane este sincronizată cu un anumit cont."</string> <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"activează/dezactivează sincronizarea"</string> <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Permite unei aplicații să modifice setările de sincronizare ale unui cont. De exemplu, cu această permisiune aplicația poate activa sincronizarea aplicației Persoane cu un anumit cont."</string> @@ -714,14 +714,14 @@ <string name="permlab_bind_incall_service" msgid="5990625112603493016">"interacțiune cu ecranul în timpul unui apel"</string> <string name="permdesc_bind_incall_service" msgid="4124917526967765162">"Permite aplicației să controleze când și cum vede utilizatorul ecranul în timpul unui apel."</string> <string name="permlab_bind_connection_service" msgid="5409268245525024736">"să interacționeze cu servicii de telefonie"</string> - <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"Permite aplicației să interacționeze cu servicii de telefonie pentru a da / a primi apeluri."</string> + <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"Permite aplicației să interacționeze cu servicii de telefonie pentru a face / a primi apeluri."</string> <string name="permlab_control_incall_experience" msgid="6436863486094352987">"oferă o experiență de utilizare în timpul unui apel"</string> <string name="permdesc_control_incall_experience" msgid="5896723643771737534">"Permite aplicației să ofere o experiență de utilizare în timpul unui apel."</string> <string name="permlab_readNetworkUsageHistory" msgid="8470402862501573795">"citește utilizarea statistică a rețelei"</string> <string name="permdesc_readNetworkUsageHistory" msgid="1112962304941637102">"Permite aplicației să citească utilizarea statistică a rețelei pentru anumite rețele și aplicații."</string> <string name="permlab_manageNetworkPolicy" msgid="6872549423152175378">"gestionează politica de rețea"</string> <string name="permdesc_manageNetworkPolicy" msgid="1865663268764673296">"Permite aplicației să gestioneze politicile de rețea și să definească regulile specifice aplicațiilor."</string> - <string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"modificați modul de calcul al utilizării rețelei"</string> + <string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"să modifice modul de calcul al utilizării rețelei"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"Permite aplicației să modifice modul în care este calculată utilizarea rețelei pentru aplicații. Nu se utilizează de aplicațiile obișnuite."</string> <string name="permlab_accessNotifications" msgid="7130360248191984741">"accesare notificări"</string> <string name="permdesc_accessNotifications" msgid="761730149268789668">"Permite aplicației să recupereze, să examineze și să șteargă notificări, inclusiv pe cele postate de alte aplicații."</string> @@ -735,7 +735,7 @@ <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"Permite proprietarului să apeleze aplicația de configurare furnizată de operator. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string> <string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"ascultă observații despre starea rețelei"</string> <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"Permite unei aplicații să asculte observații despre starea rețelei. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string> - <string name="permlab_setInputCalibration" msgid="932069700285223434">"schimbați calibrarea dispozitivului de intrare"</string> + <string name="permlab_setInputCalibration" msgid="932069700285223434">"schimbă calibrarea dispozitivului de intrare"</string> <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Permite aplicației să modifice parametrii de calibrare a ecranului tactil. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string> <string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"accesează certificatele DRM"</string> <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"Permite unei aplicații să furnizeze și să utilizeze certificate DRM. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string> @@ -749,50 +749,50 @@ <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"Permite aplicației să se conecteze la serviciile operatorului. Nu ar trebui să fie niciodată necesară pentru aplicațiile obișnuite."</string> <string name="permlab_access_notification_policy" msgid="5524112842876975537">"accesează Nu deranja"</string> <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite aplicației să citească și să scrie configurația Nu deranja."</string> - <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"porniți folosirea permisiunii de vizualizare"</string> + <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"să înceapă folosirea permisiunii de vizualizare"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite proprietarului să pornească folosirea permisiunii pentru o aplicație. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string> <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"să înceapă să examineze deciziile privind permisiunile"</string> <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Permite proprietarului să deschidă ecranul pentru a examina deciziile privind permisiunile. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string> - <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"începeți să vedeți funcțiile aplicației"</string> + <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"să vadă funcțiile aplicației"</string> <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permite proprietarului să înceapă să vadă informațiile despre funcții pentru o aplicație."</string> <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"să acceseze date de la senzori la o rată de eșantionare mare"</string> <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite aplicației să colecteze date de la senzori la o rată de eșantionare de peste 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Să seteze reguli pentru parolă"</string> - <string name="policydesc_limitPassword" msgid="4105491021115793793">"Stabiliți lungimea și tipul de caractere permise pentru parolele și codurile PIN de blocare a ecranului."</string> + <string name="policydesc_limitPassword" msgid="4105491021115793793">"Stabilește lungimea și tipul de caractere permise pentru parolele și codurile PIN de blocare a ecranului."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Să monitorizeze încercările de deblocare a ecranului"</string> - <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați tableta sau ștergeți datele acesteia dacă sunt introduse prea multe parole incorecte."</string> - <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați dispozitivul Android TV sau ștergeți toate datele de pe acesta dacă se introduc prea multe parole incorecte."</string> - <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați sistemul de infotainment sau ștergeți toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string> - <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați telefonul sau ștergeți toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string> - <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați tableta sau ștergeți toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string> - <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați dispozitivul Android TV sau ștergeți toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string> - <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați sistemul de infotainment sau ștergeți toate datele acestui profil dacă sunt introduse prea multe parole incorecte."</string> - <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați telefonul sau ștergeți toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string> + <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitorizează numărul de parole incorecte introduse la deblocarea ecranului și blochează tableta sau șterge datele acesteia dacă sunt introduse prea multe parole incorecte."</string> + <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitorizează numărul de parole incorecte introduse la deblocarea ecranului și blochează dispozitivul Android TV sau șterge toate datele de pe acesta dacă se introduc prea multe parole incorecte."</string> + <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitorizează numărul de parole incorecte introduse la deblocarea ecranului și blochează sistemul de infotainment sau șterge toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string> + <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitorizează numărul de parole incorecte introduse la deblocarea ecranului și blochează telefonul sau șterge toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string> + <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitorizează numărul de parole incorecte introduse la deblocarea ecranului și blochează tableta sau șterge toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string> + <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitorizează numărul de parole incorecte introduse la deblocarea ecranului și blochează dispozitivul Android TV sau șterge toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string> + <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitorizează numărul de parole incorecte introduse la deblocarea ecranului și blochează sistemul de infotainment sau șterge toate datele acestui profil dacă sunt introduse prea multe parole incorecte."</string> + <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitorizează numărul de parole incorecte introduse la deblocarea ecranului și blochează telefonul sau șterge toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string> <string name="policylab_resetPassword" msgid="214556238645096520">"Să schimbe blocarea ecranului"</string> - <string name="policydesc_resetPassword" msgid="4626419138439341851">"Modificați blocarea ecranului."</string> + <string name="policydesc_resetPassword" msgid="4626419138439341851">"Modifică blocarea ecranului."</string> <string name="policylab_forceLock" msgid="7360335502968476434">"Să blocheze ecranul"</string> - <string name="policydesc_forceLock" msgid="1008844760853899693">"Stabiliți modul și timpul în care se blochează ecranul."</string> + <string name="policydesc_forceLock" msgid="1008844760853899693">"Stabilește cum și când se blochează ecranul."</string> <string name="policylab_wipeData" msgid="1359485247727537311">"Să șteargă toate datele"</string> - <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Ștergeți datele de pe tabletă fără avertisment, efectuând resetarea configurării din fabrică."</string> - <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Ștergeți datele de pe dispozitivul Android TV fără avertisment, efectuând o revenire la setările din fabrică."</string> - <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Ștergeți datele din sistemul de infotainment fără avertisment, prin revenirea la setările din fabrică."</string> - <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Ștergeți datele din telefon fără avertisment, efectuând resetarea configurării din fabrică."</string> - <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Ștergeți datele de profil"</string> - <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Ștergeți datele utilizatorului"</string> - <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Ștergeți datele acestui utilizator de pe această tabletă fără avertisment."</string> - <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Ștergeți datele acestui utilizator de pe acest dispozitiv Android TV fără avertisment"</string> - <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Ștergeți datele profilului din acest sistem de infotainment fără avertisment."</string> - <string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Ștergeți datele acestui utilizator de pe acest telefon fără avertisment."</string> - <string name="policylab_setGlobalProxy" msgid="215332221188670221">"Setați serverul proxy global pentru dispozitiv"</string> - <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Setați serverul proxy global pentru dispozitiv, care să fie utilizat cât timp politica este activă. Numai proprietarul dispozitivului poate seta serverul proxy global."</string> - <string name="policylab_expirePassword" msgid="6015404400532459169">"Setați expirarea parolei pentru blocarea ecranului"</string> - <string name="policydesc_expirePassword" msgid="9136524319325960675">"Modificați frecvența cu care trebuie să se schimbe parola, codul PIN sau modelul pentru blocarea ecranului."</string> + <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Șterge datele de pe tabletă fără avertisment, efectuând resetarea configurării din fabrică."</string> + <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Șterge datele de pe dispozitivul Android TV fără avertisment, efectuând o revenire la setările din fabrică."</string> + <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Șterge datele din sistemul de infotainment fără avertisment, prin revenirea la setările din fabrică."</string> + <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Șterge datele din telefon fără avertisment, revenind la setările din fabrică."</string> + <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Șterge datele de profil"</string> + <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Șterge datele utilizatorului"</string> + <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Șterge datele acestui utilizator de pe această tabletă fără avertisment."</string> + <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Șterge datele acestui utilizator de pe acest dispozitiv Android TV fără avertisment"</string> + <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Șterge datele profilului din acest sistem de infotainment fără avertisment."</string> + <string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Șterge datele acestui utilizator de pe acest telefon fără avertisment."</string> + <string name="policylab_setGlobalProxy" msgid="215332221188670221">"Setează serverul proxy global pentru dispozitiv"</string> + <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Setează serverul proxy global pentru dispozitiv, care să fie utilizat cât timp politica este activă. Numai proprietarul dispozitivului poate seta serverul proxy global."</string> + <string name="policylab_expirePassword" msgid="6015404400532459169">"Setează expirarea parolei pentru blocarea ecranului"</string> + <string name="policydesc_expirePassword" msgid="9136524319325960675">"Modifică frecvența cu care trebuie să se schimbe parola, codul PIN sau modelul pentru blocarea ecranului."</string> <string name="policylab_encryptedStorage" msgid="9012936958126670110">"Să seteze criptarea stocării"</string> <string name="policydesc_encryptedStorage" msgid="1102516950740375617">"Necesită ca datele aplicației stocate să fie criptate."</string> <string name="policylab_disableCamera" msgid="5749486347810162018">"Să dezactiveze camerele foto"</string> - <string name="policydesc_disableCamera" msgid="3204405908799676104">"Împiedicați utilizarea camerelor foto de pe dispozitiv."</string> + <string name="policydesc_disableCamera" msgid="3204405908799676104">"Împiedică folosirea camerelor foto de pe dispozitiv."</string> <string name="policylab_disableKeyguardFeatures" msgid="5071855750149949741">"Să oprească funcții de blocare ecran"</string> - <string name="policydesc_disableKeyguardFeatures" msgid="6641673177041195957">"Împiedicați folosirea unor funcții de blocare a ecranului."</string> + <string name="policydesc_disableKeyguardFeatures" msgid="6641673177041195957">"Împiedică folosirea unor funcții de blocare a ecranului."</string> <string-array name="phoneTypes"> <item msgid="8996339953292723951">"Domiciliu"</item> <item msgid="7740243458912727194">"Mobil"</item> @@ -911,23 +911,23 @@ <string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"Introdu codul PUK și noul cod PIN"</string> <string name="keyguard_password_enter_puk_prompt" msgid="2825313071899938305">"Codul PUK"</string> <string name="keyguard_password_enter_pin_prompt" msgid="5505434724229581207">"Noul cod PIN"</string> - <string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"Atingeți ca să introduceți parola"</font></string> + <string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"Atinge ca să introduci parola"</font></string> <string name="keyguard_password_enter_password_code" msgid="2751130557661643482">"Introdu parola pentru a debloca"</string> <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"Introdu codul PIN pentru a debloca"</string> <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"Cod PIN incorect."</string> - <string name="keyguard_label_text" msgid="3841953694564168384">"Pentru a debloca, apăsați Meniu, apoi 0."</string> + <string name="keyguard_label_text" msgid="3841953694564168384">"Pentru a debloca, apasă Meniu, apoi 0."</string> <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"Număr de urgență"</string> <string name="lockscreen_carrier_default" msgid="6192313772955399160">"Fără semnal"</string> <string name="lockscreen_screen_locked" msgid="7364905540516041817">"Ecranul este blocat."</string> - <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Apăsați Meniu pentru a debloca sau pentru a efectua apeluri de urgență."</string> - <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Apăsați Meniu pentru deblocare."</string> - <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenați modelul pentru a debloca"</string> + <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Apasă Meniu pentru a debloca sau pentru a efectua apeluri de urgență."</string> + <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Apasă Meniu pentru deblocare."</string> + <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenează modelul pentru a debloca"</string> <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgență"</string> - <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Reveniți la apel"</string> + <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Revino la apel"</string> <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Corect!"</string> - <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Încercați din nou"</string> - <string name="lockscreen_password_wrong" msgid="8605355913868947490">"Încercați din nou"</string> - <string name="lockscreen_storage_locked" msgid="634993789186443380">"Deblocați pentru toate funcțiile și datele"</string> + <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Încearcă din nou"</string> + <string name="lockscreen_password_wrong" msgid="8605355913868947490">"Încearcă din nou"</string> + <string name="lockscreen_storage_locked" msgid="634993789186443380">"Deblochează pentru toate funcțiile și datele"</string> <string name="faceunlock_multiple_failures" msgid="681991538434031708">"S-a depășit numărul maxim de încercări pentru Deblocare facială"</string> <string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Fără SIM"</string> <string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Nu există card SIM în computerul tablet PC."</string> @@ -936,44 +936,44 @@ <string name="lockscreen_missing_sim_instructions" msgid="8473601862688263903">"Introdu un card SIM."</string> <string name="lockscreen_missing_sim_instructions_long" msgid="3664999892038416334">"Cardul SIM lipsește sau nu poate fi citit. Introdu un card SIM."</string> <string name="lockscreen_permanent_disabled_sim_message_short" msgid="3812893366715730539">"Card SIM inutilizabil."</string> - <string name="lockscreen_permanent_disabled_sim_instructions" msgid="4358929052509450807">"Cardul dvs. SIM este dezactivat definitiv.\n Contactați furnizorul de servicii wireless pentru a obține un alt card SIM."</string> + <string name="lockscreen_permanent_disabled_sim_instructions" msgid="4358929052509450807">"Cardul SIM este dezactivat definitiv.\n Contactează furnizorul de servicii wireless pentru a obține un alt card SIM."</string> <string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"Melodia anterioară"</string> <string name="lockscreen_transport_next_description" msgid="2931509904881099919">"Melodia următoare"</string> <string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pauză"</string> - <string name="lockscreen_transport_play_description" msgid="106868788691652733">"Redați"</string> - <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"Opriți"</string> - <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Derulați"</string> - <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Derulați rapid înainte"</string> + <string name="lockscreen_transport_play_description" msgid="106868788691652733">"Redă"</string> + <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"Oprește"</string> + <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Derulează"</string> + <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Derulează rapid înainte"</string> <string name="emergency_calls_only" msgid="3057351206678279851">"Numai apeluri de urgență"</string> <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Rețea blocată"</string> <string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"Cardul SIM este blocat cu codul PUK."</string> - <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Consultați Ghidul de utilizare sau contactați Serviciul de relații cu clienții."</string> + <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Consultă Ghidul de utilizare sau contactează asistența pentru clienți."</string> <string name="lockscreen_sim_locked_message" msgid="3160196135801185938">"Cardul SIM este blocat."</string> <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"Se deblochează cardul SIM..."</string> - <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> - <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"Ați introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> - <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"Ați introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> - <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați tableta cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> - <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați dispozitivul Android TV prin conectarea la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> - <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> - <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, aceasta va fi resetată la setările prestabilite din fabrică, iar toate datele de utilizator vor fi pierdute."</string> - <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va reveni la setările din fabrică, iar toate datele de utilizator se vor pierde."</string> - <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va fi resetat la setările prestabilite din fabrică, iar toate datele de utilizator vor fi pierdute."</string> - <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Tableta va fi acum resetată la setările prestabilite din fabrică."</string> - <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. Acesta va reveni la setările din fabrică."</string> - <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Acesta va fi acum resetat la setările prestabilite din fabrică."</string> - <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"Încercați din nou peste <xliff:g id="NUMBER">%d</xliff:g> secunde."</string> - <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Ați uitat modelul?"</string> + <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> + <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"Ai introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> + <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"Ai introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> + <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, ți se va solicita să deblochezi tableta cu ajutorul datelor de conectare la Google.\n\n Încearcă din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> + <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, ți se va solicita să deblochezi dispozitivul Android TV prin conectarea la Google.\n\n Încearcă din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> + <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, ți se va solicita să deblochezi telefonul cu ajutorul datelor de conectare la Google.\n\n Încearcă din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> + <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, aceasta va reveni la setările din fabrică, iar toate datele de utilizator se vor pierde."</string> + <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va reveni la setările din fabrică, iar toate datele de utilizator se vor pierde."</string> + <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va reveni la setările din fabrică, iar toate datele de utilizator se vor pierde."</string> + <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Tableta va reveni acum la setările din fabrică."</string> + <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. Acesta va reveni la setările din fabrică."</string> + <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Acesta va reveni acum la setările din fabrică."</string> + <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"Încearcă din nou peste <xliff:g id="NUMBER">%d</xliff:g> secunde."</string> + <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Ai uitat modelul?"</string> <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Deblocare cont"</string> <string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"Prea multe încercări de desenare a modelului"</string> - <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Pentru a debloca, conectați-vă folosind Contul Google."</string> + <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Pentru a debloca, conectează-te folosind Contul Google."</string> <string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"Nume de utilizator (e-mail)"</string> <string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"Parolă"</string> <string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"Conectează-te"</string> <string name="lockscreen_glogin_invalid_input" msgid="4369219936865697679">"Nume de utilizator sau parolă nevalide."</string> - <string name="lockscreen_glogin_account_recovery_hint" msgid="1683405808525090649">"Ați uitat numele de utilizator sau parola?\nAccesați "<b>"google.com/accounts/recovery"</b>"."</string> + <string name="lockscreen_glogin_account_recovery_hint" msgid="1683405808525090649">"Ai uitat numele de utilizator sau parola?\nAccesează "<b>"google.com/accounts/recovery"</b>"."</string> <string name="lockscreen_glogin_checking_password" msgid="2607271802803381645">"Se verifică..."</string> - <string name="lockscreen_unlock_label" msgid="4648257878373307582">"Deblocați"</string> + <string name="lockscreen_unlock_label" msgid="4648257878373307582">"Deblochează"</string> <string name="lockscreen_sound_on_label" msgid="1660281470535492430">"Sunet activat"</string> <string name="lockscreen_sound_off_label" msgid="2331496559245450053">"Sunet dezactivat"</string> <string name="lockscreen_access_pattern_start" msgid="3778502525702613399">"Desenarea modelului a început"</string> @@ -983,7 +983,7 @@ <string name="lockscreen_access_pattern_detected" msgid="3931150554035194012">"Modelul a fost desenat"</string> <string name="lockscreen_access_pattern_area" msgid="1288780416685002841">"Zonă model."</string> <string name="keyguard_accessibility_widget_changed" msgid="7298011259508200234">"%1$s. Widget %2$d din %3$d."</string> - <string name="keyguard_accessibility_add_widget" msgid="8245795023551343672">"Adăugați un widget."</string> + <string name="keyguard_accessibility_add_widget" msgid="8245795023551343672">"Adaugă un widget."</string> <string name="keyguard_accessibility_widget_empty_slot" msgid="544239307077644480">"Gol"</string> <string name="keyguard_accessibility_unlock_area_expanded" msgid="7768634718706488951">"Zona de deblocare a fost extinsă."</string> <string name="keyguard_accessibility_unlock_area_collapsed" msgid="4729922043778400434">"Zona de deblocare a fost restrânsă."</string> @@ -995,7 +995,7 @@ <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"A început reordonarea widgeturilor."</string> <string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"Reordonarea widgeturilor s-a încheiat."</string> <string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"Widgetul <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> a fost eliminat."</string> - <string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Extindeți zona de deblocare."</string> + <string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Extinde zona de deblocare."</string> <string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Deblocare prin glisare."</string> <string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Deblocare cu model."</string> <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"Deblocare facială."</string> @@ -1015,18 +1015,18 @@ <string name="factorytest_failed" msgid="3190979160945298006">"Testarea de fabrică nu a reușit"</string> <string name="factorytest_not_system" msgid="5658160199925519869">"Acțiunea FACTORY_TEST este acceptată doar pentru pachetele instalate în /system/app."</string> <string name="factorytest_no_action" msgid="339252838115675515">"Nu s-a găsit niciun pachet care să ofere acțiunea FACTORY_TEST."</string> - <string name="factorytest_reboot" msgid="2050147445567257365">"Reporniți"</string> + <string name="factorytest_reboot" msgid="2050147445567257365">"Repornește"</string> <string name="js_dialog_title" msgid="7464775045615023241">"La pagina de la „<xliff:g id="TITLE">%s</xliff:g>” apare:"</string> <string name="js_dialog_title_default" msgid="3769524569903332476">"JavaScript"</string> - <string name="js_dialog_before_unload_title" msgid="7012587995876771246">"Confirmați părăsirea paginii"</string> - <string name="js_dialog_before_unload_positive_button" msgid="4274257182303565509">"Părăsiți această pagină"</string> - <string name="js_dialog_before_unload_negative_button" msgid="3873765747622415310">"Rămâneți în această pagină"</string> - <string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nSigur doriți să părăsiți această pagină?"</string> - <string name="save_password_label" msgid="9161712335355510035">"Confirmați"</string> - <string name="double_tap_toast" msgid="7065519579174882778">"Sfat: măriți și micșorați prin dublă atingere."</string> + <string name="js_dialog_before_unload_title" msgid="7012587995876771246">"Confirmă părăsirea paginii"</string> + <string name="js_dialog_before_unload_positive_button" msgid="4274257182303565509">"Părăsește această pagină"</string> + <string name="js_dialog_before_unload_negative_button" msgid="3873765747622415310">"Rămâi în această pagină"</string> + <string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nSigur părăsești această pagină?"</string> + <string name="save_password_label" msgid="9161712335355510035">"Confirmă"</string> + <string name="double_tap_toast" msgid="7065519579174882778">"Sfat: mărește și micșorează prin dublă atingere."</string> <string name="autofill_this_form" msgid="3187132440451621492">"Automat"</string> <string name="setup_autofill" msgid="5431369130866618567">"Conf.Compl.auto."</string> - <string name="autofill_window_title" msgid="4379134104008111961">"Completați automat cu <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string> + <string name="autofill_window_title" msgid="4379134104008111961">"Completează automat cu <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string> <string name="autofill_address_name_separator" msgid="8190155636149596125">" "</string> <string name="autofill_address_summary_name_format" msgid="3402882515222673691">"$1$2$3"</string> <string name="autofill_address_summary_separator" msgid="760522655085707045">", "</string> @@ -1055,11 +1055,11 @@ <string name="permdesc_addVoicemail" msgid="5470312139820074324">"Permite aplicației să adauge mesaje în Mesaje primite în mesageria vocală."</string> <string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"modificare permisiuni pentru locația geografică a browserului"</string> <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"Permite aplicației să modifice permisiunile privind locația geografică a browserului. Aplicațiile rău intenționate pot utiliza această permisiune pentru a permite trimiterea informațiilor privind locația către site-uri web arbitrare."</string> - <string name="save_password_message" msgid="2146409467245462965">"Doriți ca browserul să rețină această parolă?"</string> + <string name="save_password_message" msgid="2146409467245462965">"Vrei ca browserul să rețină această parolă?"</string> <string name="save_password_notnow" msgid="2878327088951240061">"Nu acum"</string> - <string name="save_password_remember" msgid="6490888932657708341">"Rețineți"</string> + <string name="save_password_remember" msgid="6490888932657708341">"Reține"</string> <string name="save_password_never" msgid="6776808375903410659">"Niciodată"</string> - <string name="open_permission_deny" msgid="5136793905306987251">"Nu aveți permisiunea de a deschide această pagină."</string> + <string name="open_permission_deny" msgid="5136793905306987251">"Nu ai permisiunea de a deschide această pagină."</string> <string name="text_copied" msgid="2531420577879738860">"Text copiat în clipboard."</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a inserat date din <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a inserat din clipboard"</string> @@ -1077,16 +1077,16 @@ <string name="menu_space_shortcut_label" msgid="5949311515646872071">"spațiu"</string> <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"enter"</string> <string name="menu_delete_shortcut_label" msgid="4365787714477739080">"delete"</string> - <string name="search_go" msgid="2141477624421347086">"Căutați"</string> - <string name="search_hint" msgid="455364685740251925">"Căutați…"</string> - <string name="searchview_description_search" msgid="1045552007537359343">"Căutați"</string> + <string name="search_go" msgid="2141477624421347086">"Caută"</string> + <string name="search_hint" msgid="455364685740251925">"Caută…"</string> + <string name="searchview_description_search" msgid="1045552007537359343">"Caută"</string> <string name="searchview_description_query" msgid="7430242366971716338">"Interogare de căutare"</string> - <string name="searchview_description_clear" msgid="1989371719192982900">"Ștergeți interogarea"</string> + <string name="searchview_description_clear" msgid="1989371719192982900">"Șterge interogarea"</string> <string name="searchview_description_submit" msgid="6771060386117334686">"Trimite interogarea"</string> <string name="searchview_description_voice" msgid="42360159504884679">"Căutare vocală"</string> - <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Activați Explorați prin atingere?"</string> - <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> dorește să activeze funcția Explorați prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacționa cu tableta."</string> - <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> dorește să activeze funcția Explorați prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacționa cu telefonul."</string> + <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Activezi Explorează prin atingere?"</string> + <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vrea să activeze funcția Explorează prin atingere. Când e activată, poți auzi sau vedea descrieri pentru ceea ce se află sub degetul tău sau poți face gesturi pentru a interacționa cu tableta."</string> + <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> dorește să activeze funcția Explorează prin atingere. Când aceasta e activată, poți auzi sau vedea descrieri pentru ceea ce se află sub degetul tău sau poți face gesturi pentru a interacționa cu telefonul."</string> <string name="oneMonthDurationPast" msgid="4538030857114635777">"cu 1 lună în urmă"</string> <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Cu mai mult de 1 lună în urmă"</string> <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Ultima zi}few{Ultimele # zile}other{Ultimele # de zile}}"</string> @@ -1099,9 +1099,9 @@ <string name="days" msgid="4570879797423034973">" zile"</string> <string name="hour" msgid="7796325297097314653">"oră"</string> <string name="hours" msgid="8517014849629200683">"ore"</string> - <string name="minute" msgid="8369209540986467610">"min"</string> + <string name="minute" msgid="8369209540986467610">"min."</string> <string name="minutes" msgid="3456532942641808971">"min."</string> - <string name="second" msgid="9210875257112211713">"sec"</string> + <string name="second" msgid="9210875257112211713">"sec."</string> <string name="seconds" msgid="2175052687727971048">"sec."</string> <string name="week" msgid="907127093960923779">"săptămână"</string> <string name="weeks" msgid="3516247214269821391">"săptămâni"</string> @@ -1126,7 +1126,7 @@ <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# an}few{# ani}other{# de ani}}"</string> <string name="VideoView_error_title" msgid="5750686717225068016">"Problemă video"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Acest fișier video nu este valid pentru a fi transmis în flux către acest dispozitiv."</string> - <string name="VideoView_error_text_unknown" msgid="7658683339707607138">"Nu puteți reda acest videoclip"</string> + <string name="VideoView_error_text_unknown" msgid="7658683339707607138">"Nu poți reda acest videoclip"</string> <string name="VideoView_error_button" msgid="5138809446603764272">"OK"</string> <string name="relative_time" msgid="8572030016028033243">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="noon" msgid="8365974533050605886">"prânz"</string> @@ -1135,31 +1135,31 @@ <string name="Midnight" msgid="8176019203622191377">"Miezul nopții"</string> <string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> - <string name="selectAll" msgid="1532369154488982046">"Selectați-le pe toate"</string> - <string name="cut" msgid="2561199725874745819">"Decupați"</string> - <string name="copy" msgid="5472512047143665218">"Copiați"</string> + <string name="selectAll" msgid="1532369154488982046">"Selectează-le pe toate"</string> + <string name="cut" msgid="2561199725874745819">"Decupează"</string> + <string name="copy" msgid="5472512047143665218">"Copiază"</string> <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Eroare la copierea în clipboard"</string> - <string name="paste" msgid="461843306215520225">"Inserați"</string> - <string name="paste_as_plain_text" msgid="7664800665823182587">"Inserați ca text simplu"</string> - <string name="replace" msgid="7842675434546657444">"Înlocuiți..."</string> - <string name="delete" msgid="1514113991712129054">"Ștergeți"</string> - <string name="copyUrl" msgid="6229645005987260230">"Copiați adresa URL"</string> - <string name="selectTextMode" msgid="3225108910999318778">"Selectați text"</string> + <string name="paste" msgid="461843306215520225">"Inserează"</string> + <string name="paste_as_plain_text" msgid="7664800665823182587">"Inserează ca text simplu"</string> + <string name="replace" msgid="7842675434546657444">"Înlocuiește..."</string> + <string name="delete" msgid="1514113991712129054">"Șterge"</string> + <string name="copyUrl" msgid="6229645005987260230">"Copiază adresa URL"</string> + <string name="selectTextMode" msgid="3225108910999318778">"Selectează text"</string> <string name="undo" msgid="3175318090002654673">"Anulează"</string> - <string name="redo" msgid="7231448494008532233">"Repetați"</string> + <string name="redo" msgid="7231448494008532233">"Repetă"</string> <string name="autofill" msgid="511224882647795296">"Completare automată"</string> <string name="textSelectionCABTitle" msgid="5151441579532476940">"Selectare text"</string> - <string name="addToDictionary" msgid="8041821113480950096">"Adăugați în dicționar"</string> - <string name="deleteText" msgid="4200807474529938112">"Ștergeți"</string> + <string name="addToDictionary" msgid="8041821113480950096">"Adaugă în dicționar"</string> + <string name="deleteText" msgid="4200807474529938112">"Șterge"</string> <string name="inputMethod" msgid="1784759500516314751">"Metodă de intrare"</string> <string name="editTextMenuTitle" msgid="857666911134482176">"Acțiuni pentru text"</string> <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Înapoi"</string> - <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Comutați metoda de introducere a textului"</string> + <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Schimbă metoda de introducere"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Spațiul de stocare aproape ocupat"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Este posibil ca unele funcții de sistem să nu funcționeze"</string> - <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Spațiu de stocare insuficient pentru sistem. Asigurați-vă că aveți 250 MB de spațiu liber și reporniți."</string> + <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Spațiu de stocare insuficient pentru sistem. Asigură-te că ai 250 MB de spațiu liber și repornește."</string> <string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> rulează acum"</string> - <string name="app_running_notification_text" msgid="5120815883400228566">"Atingeți pentru mai multe informații sau pentru a opri aplicația."</string> + <string name="app_running_notification_text" msgid="5120815883400228566">"Atinge pentru mai multe informații sau pentru a opri aplicația."</string> <string name="ok" msgid="2646370155170753815">"OK"</string> <string name="cancel" msgid="6908697720451760115">"Anulează"</string> <string name="yes" msgid="9069828999585032361">"OK"</string> @@ -1174,34 +1174,34 @@ <string name="not_selected" msgid="410652016565864475">"neselectat"</string> <string name="in_progress" msgid="2149208189184319441">"în curs"</string> <string name="whichApplication" msgid="5432266899591255759">"Finalizare acțiune utilizând"</string> - <string name="whichApplicationNamed" msgid="6969946041713975681">"Finalizați acțiunea utilizând %1$s"</string> - <string name="whichApplicationLabel" msgid="7852182961472531728">"Finalizați acțiunea"</string> - <string name="whichViewApplication" msgid="5733194231473132945">"Deschideți cu"</string> - <string name="whichViewApplicationNamed" msgid="415164730629690105">"Deschideți cu %1$s"</string> - <string name="whichViewApplicationLabel" msgid="7367556735684742409">"Deschideți"</string> - <string name="whichOpenHostLinksWith" msgid="7645631470199397485">"Deschideți linkurile <xliff:g id="HOST">%1$s</xliff:g> cu"</string> - <string name="whichOpenLinksWith" msgid="1120936181362907258">"Deschideți linkurile cu"</string> - <string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Deschideți linkurile cu <xliff:g id="APPLICATION">%1$s</xliff:g>"</string> - <string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Deschideți linkurile <xliff:g id="HOST">%1$s</xliff:g> cu <xliff:g id="APPLICATION">%2$s</xliff:g>"</string> - <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Permiteți accesul"</string> - <string name="whichEditApplication" msgid="6191568491456092812">"Editați cu"</string> - <string name="whichEditApplicationNamed" msgid="8096494987978521514">"Editați cu %1$s"</string> - <string name="whichEditApplicationLabel" msgid="1463288652070140285">"Editați"</string> + <string name="whichApplicationNamed" msgid="6969946041713975681">"Finalizează acțiunea folosind %1$s"</string> + <string name="whichApplicationLabel" msgid="7852182961472531728">"Finalizează acțiunea"</string> + <string name="whichViewApplication" msgid="5733194231473132945">"Deschide cu"</string> + <string name="whichViewApplicationNamed" msgid="415164730629690105">"Deschide cu %1$s"</string> + <string name="whichViewApplicationLabel" msgid="7367556735684742409">"Deschide"</string> + <string name="whichOpenHostLinksWith" msgid="7645631470199397485">"Deschide linkurile <xliff:g id="HOST">%1$s</xliff:g> cu"</string> + <string name="whichOpenLinksWith" msgid="1120936181362907258">"Deschide linkurile cu"</string> + <string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Deschide linkurile cu <xliff:g id="APPLICATION">%1$s</xliff:g>"</string> + <string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Deschide linkurile <xliff:g id="HOST">%1$s</xliff:g> cu <xliff:g id="APPLICATION">%2$s</xliff:g>"</string> + <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Permite accesul"</string> + <string name="whichEditApplication" msgid="6191568491456092812">"Editează cu"</string> + <string name="whichEditApplicationNamed" msgid="8096494987978521514">"Editează cu %1$s"</string> + <string name="whichEditApplicationLabel" msgid="1463288652070140285">"Editează"</string> <string name="whichSendApplication" msgid="4143847974460792029">"Trimite"</string> - <string name="whichSendApplicationNamed" msgid="4470386782693183461">"Distribuiți cu %1$s"</string> + <string name="whichSendApplicationNamed" msgid="4470386782693183461">"Distribuie cu %1$s"</string> <string name="whichSendApplicationLabel" msgid="7467813004769188515">"Trimite"</string> <string name="whichSendToApplication" msgid="77101541959464018">"Trimite folosind"</string> <string name="whichSendToApplicationNamed" msgid="3385686512014670003">"Trimite folosind %1$s"</string> <string name="whichSendToApplicationLabel" msgid="3543240188816513303">"Trimite"</string> - <string name="whichHomeApplication" msgid="8276350727038396616">"Selectați o aplicație de pe ecranul de pornire"</string> - <string name="whichHomeApplicationNamed" msgid="5855990024847433794">"Utilizați %1$s ca ecran de pornire"</string> - <string name="whichHomeApplicationLabel" msgid="8907334282202933959">"Fotografiați"</string> - <string name="whichImageCaptureApplication" msgid="2737413019463215284">"Fotografiați cu"</string> - <string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"Fotografiați cu %1$s"</string> - <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Fotografiați"</string> + <string name="whichHomeApplication" msgid="8276350727038396616">"Selectează o aplicație de pe ecranul de pornire"</string> + <string name="whichHomeApplicationNamed" msgid="5855990024847433794">"Folosește %1$s ca ecran de pornire"</string> + <string name="whichHomeApplicationLabel" msgid="8907334282202933959">"Fotografiază"</string> + <string name="whichImageCaptureApplication" msgid="2737413019463215284">"Fotografiază cu"</string> + <string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"Fotografiază cu %1$s"</string> + <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Fotografiază"</string> <string name="alwaysUse" msgid="3153558199076112903">"Se utilizează în mod prestabilit pentru această acțiune."</string> - <string name="use_a_different_app" msgid="4987790276170972776">"Utilizați altă aplicație"</string> - <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Ștergeți setările prestabilite din Setări de sistem > Aplicații > Descărcate."</string> + <string name="use_a_different_app" msgid="4987790276170972776">"Folosește altă aplicație"</string> + <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Șterge setările prestabilite din Setări de sistem > Aplicații > Descărcate."</string> <string name="chooseActivity" msgid="8563390197659779956">"Alege o acțiune"</string> <string name="chooseUsbActivity" msgid="2096269989990986612">"Alege o aplicație pentru dispozitivul USB"</string> <string name="noApplications" msgid="1186909265235544019">"Această acțiune nu poate fi efectuată de nicio aplicație."</string> @@ -1209,32 +1209,32 @@ <string name="aerr_process" msgid="4268018696970966407">"<xliff:g id="PROCESS">%1$s</xliff:g> s-a oprit"</string> <string name="aerr_application_repeated" msgid="7804378743218496566">"<xliff:g id="APPLICATION">%1$s</xliff:g> se oprește încontinuu"</string> <string name="aerr_process_repeated" msgid="1153152413537954974">"<xliff:g id="PROCESS">%1$s</xliff:g> se oprește încontinuu"</string> - <string name="aerr_restart" msgid="2789618625210505419">"Deschideți din nou aplicația"</string> + <string name="aerr_restart" msgid="2789618625210505419">"Deschide din nou aplicația"</string> <string name="aerr_report" msgid="3095644466849299308">"Trimite feedback"</string> - <string name="aerr_close" msgid="3398336821267021852">"Închideți"</string> - <string name="aerr_mute" msgid="2304972923480211376">"Dezactivați până la repornirea dispozitivului"</string> - <string name="aerr_wait" msgid="3198677780474548217">"Așteptați"</string> - <string name="aerr_close_app" msgid="8318883106083050970">"Închideți aplicația"</string> + <string name="aerr_close" msgid="3398336821267021852">"Închide"</string> + <string name="aerr_mute" msgid="2304972923480211376">"Dezactivează până la repornirea dispozitivului"</string> + <string name="aerr_wait" msgid="3198677780474548217">"Așteaptă"</string> + <string name="aerr_close_app" msgid="8318883106083050970">"Închide aplicația"</string> <string name="anr_title" msgid="7290329487067300120"></string> <string name="anr_activity_application" msgid="8121716632960340680">"<xliff:g id="APPLICATION">%2$s</xliff:g> nu răspunde"</string> <string name="anr_activity_process" msgid="3477362583767128667">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nu răspunde"</string> <string name="anr_application_process" msgid="4978772139461676184">"<xliff:g id="APPLICATION">%1$s</xliff:g> nu răspunde"</string> <string name="anr_process" msgid="1664277165911816067">"Procesul <xliff:g id="PROCESS">%1$s</xliff:g> nu răspunde"</string> <string name="force_close" msgid="9035203496368973803">"OK"</string> - <string name="report" msgid="2149194372340349521">"Raportați"</string> - <string name="wait" msgid="7765985809494033348">"Așteptați"</string> - <string name="webpage_unresponsive" msgid="7850879412195273433">"Pagina a devenit inactivă.\n\nDoriți să o închideți?"</string> + <string name="report" msgid="2149194372340349521">"Raportează"</string> + <string name="wait" msgid="7765985809494033348">"Așteaptă"</string> + <string name="webpage_unresponsive" msgid="7850879412195273433">"Pagina a devenit inactivă.\n\nO închizi?"</string> <string name="launch_warning_title" msgid="6725456009564953595">"Aplicație redirecționată"</string> <string name="launch_warning_replace" msgid="3073392976283203402">"<xliff:g id="APP_NAME">%1$s</xliff:g> funcționează acum."</string> <string name="launch_warning_original" msgid="3332206576800169626">"<xliff:g id="APP_NAME">%1$s</xliff:g> a fost lansată inițial."</string> <string name="screen_compat_mode_scale" msgid="8627359598437527726">"Scară"</string> - <string name="screen_compat_mode_show" msgid="5080361367584709857">"Afișați întotdeauna"</string> - <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Reactivați acest mod din Setări de sistem > Aplicații > Descărcate."</string> + <string name="screen_compat_mode_show" msgid="5080361367584709857">"Afișează întotdeauna"</string> + <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Reactivează acest mod din Setări de sistem > Aplicații > Descărcate."</string> <string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu acceptă setarea actuală pentru Dimensiunea afișării și este posibil să aibă un comportament neașteptat."</string> <string name="unsupported_display_size_show" msgid="980129850974919375">"Afișează întotdeauna"</string> <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> a fost concepută pentru o versiune incompatibilă de sistem de operare Android și este posibil să se comporte în mod neprevăzut. Poate fi disponibilă o versiune actualizată a aplicației."</string> - <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Afișați întotdeauna"</string> - <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Căutați actualizări"</string> + <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Afișează întotdeauna"</string> + <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Caută actualizări"</string> <string name="smv_application" msgid="3775183542777792638">"Aplicația <xliff:g id="APPLICATION">%1$s</xliff:g> (procesul <xliff:g id="PROCESS">%2$s</xliff:g>) a încălcat propria politică StrictMode."</string> <string name="smv_process" msgid="1398801497130695446">"Procesul <xliff:g id="PROCESS">%1$s</xliff:g> a încălcat propria politică StrictMode."</string> <string name="android_upgrading_title" product="default" msgid="7279077384220829683">"Se actualizează telefonul.…"</string> @@ -1251,27 +1251,27 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Se pregătește <xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Se pornesc aplicațiile."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Se finalizează pornirea."</string> - <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Ați apăsat butonul de pornire. De obicei, această acțiune dezactivează ecranul.\n\nAtingeți ușor când vă configurați amprenta."</string> + <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Ai apăsat butonul de pornire. De obicei, astfel se dezactivează ecranul.\n\nAtinge ușor când îți configurezi amprenta."</string> <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Ca să termini configurarea, dezactivează ecranul"</string> <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Dezactivează"</string> - <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuați cu verificarea amprentei?"</string> - <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Ați apăsat butonul de pornire. De obicei, această acțiune dezactivează ecranul.\n\nAtingeți ușor pentru verificarea amprentei."</string> - <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Dezactivați ecranul"</string> - <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuați"</string> + <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continui cu verificarea amprentei?"</string> + <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Ai apăsat butonul de pornire. De obicei, astfel se dezactivează ecranul.\n\nAtinge ușor pentru verificarea amprentei."</string> + <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Dezactivează ecranul"</string> + <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuă"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"Rulează <xliff:g id="APP">%1$s</xliff:g>"</string> - <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Atingeți pentru a reveni la joc"</string> + <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Atinge pentru a reveni la joc"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Alege jocul"</string> <string name="heavy_weight_switcher_text" msgid="6814316627367160126">"Pentru o performanță mai bună, se poate deschide un singur joc odată."</string> - <string name="old_app_action" msgid="725331621042848590">"Reveniți la <xliff:g id="OLD_APP">%1$s</xliff:g>"</string> - <string name="new_app_action" msgid="547772182913269801">"Deschideți <xliff:g id="NEW_APP">%1$s</xliff:g>"</string> + <string name="old_app_action" msgid="725331621042848590">"Revino la <xliff:g id="OLD_APP">%1$s</xliff:g>"</string> + <string name="new_app_action" msgid="547772182913269801">"Deschide <xliff:g id="NEW_APP">%1$s</xliff:g>"</string> <string name="new_app_description" msgid="1958903080400806644">"<xliff:g id="OLD_APP">%1$s</xliff:g> se va închide fără să salveze"</string> <string name="dump_heap_notification" msgid="5316644945404825032">"<xliff:g id="PROC">%1$s</xliff:g> a depășit limita de memorie"</string> <string name="dump_heap_ready_notification" msgid="2302452262927390268">"Datele privind memoria heap <xliff:g id="PROC">%1$s</xliff:g> sunt gata"</string> - <string name="dump_heap_notification_detail" msgid="8431586843001054050">"Datele privind memoria au fost culese. Atingeți pentru a trimite."</string> + <string name="dump_heap_notification_detail" msgid="8431586843001054050">"Datele privind memoria au fost culese. Atinge pentru a trimite."</string> <string name="dump_heap_title" msgid="4367128917229233901">"Trimiți datele privind memoria?"</string> - <string name="dump_heap_text" msgid="1692649033835719336">"Procesul <xliff:g id="PROC">%1$s</xliff:g> și-a depășit limita de memorie de <xliff:g id="SIZE">%2$s</xliff:g>. Sunt disponibile datele privind memoria heap, pe care le puteți trimite dezvoltatorului. Atenție: aceste date privind memoria heap pot conține informații cu caracter personal la care aplicația are acces."</string> - <string name="dump_heap_system_text" msgid="6805155514925350849">"Procesul <xliff:g id="PROC">%1$s</xliff:g> a depășit limita de memorie de <xliff:g id="SIZE">%2$s</xliff:g>. Sunt disponibile datele privind memoria heap, pe care le puteți distribui. Atenție: aceste date privind memoria heap pot conține informații cu caracter personal sensibile la care procesul are acces și care pot include ceea ce tastați."</string> - <string name="dump_heap_ready_text" msgid="5849618132123045516">"Sunt disponibile datele privind memoria heap a procesului <xliff:g id="PROC">%1$s</xliff:g>, pe care le puteți distribui. Atenție: aceste date privind memoria heap pot conține informații cu caracter personal sensibile la care procesul are acces și care pot include ceea ce tastați."</string> + <string name="dump_heap_text" msgid="1692649033835719336">"Procesul <xliff:g id="PROC">%1$s</xliff:g> și-a depășit limita de memorie de <xliff:g id="SIZE">%2$s</xliff:g>. Sunt disponibile datele privind memoria heap, pe care le poți trimite dezvoltatorului. Atenție: aceste date privind memoria heap pot conține informații cu caracter personal la care aplicația are acces."</string> + <string name="dump_heap_system_text" msgid="6805155514925350849">"Procesul <xliff:g id="PROC">%1$s</xliff:g> a depășit limita de memorie de <xliff:g id="SIZE">%2$s</xliff:g>. Sunt disponibile datele privind memoria heap, pe care le poți distribui. Atenție: aceste date privind memoria heap pot conține informații cu caracter personal sensibile la care procesul are acces și care pot include ceea ce tastezi."</string> + <string name="dump_heap_ready_text" msgid="5849618132123045516">"Sunt disponibile datele privind memoria heap a procesului <xliff:g id="PROC">%1$s</xliff:g>, pe care le poți distribui. Atenție: aceste date privind memoria heap pot conține informații cu caracter personal sensibile la care procesul are acces și care pot include ceea ce tastezi."</string> <string name="sendText" msgid="493003724401350724">"Alege o acțiune pentru text"</string> <string name="volume_ringtone" msgid="134784084629229029">"Volum sonerie"</string> <string name="volume_music" msgid="7727274216734955095">"Volum media"</string> @@ -1294,17 +1294,17 @@ <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Sunete de alarmă"</string> <string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Sunete pentru notificare"</string> <string name="ringtone_unknown" msgid="5059495249862816475">"Necunoscut"</string> - <string name="wifi_available_sign_in" msgid="381054692557675237">"Conectați-vă la rețeaua Wi-Fi"</string> - <string name="network_available_sign_in" msgid="1520342291829283114">"Conectați-vă la rețea"</string> + <string name="wifi_available_sign_in" msgid="381054692557675237">"Conectează-te la rețeaua Wi-Fi"</string> + <string name="network_available_sign_in" msgid="1520342291829283114">"Conectează-te la rețea"</string> <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) --> <skip /> <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nu are acces la internet"</string> - <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Atingeți pentru opțiuni"</string> + <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Atinge pentru opțiuni"</string> <string name="mobile_no_internet" msgid="4014455157529909781">"Rețeaua mobilă nu are acces la internet"</string> <string name="other_networks_no_internet" msgid="6698711684200067033">"Rețeaua nu are acces la internet"</string> <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Serverul DNS privat nu poate fi accesat"</string> <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> are conectivitate limitată"</string> - <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Atingeți pentru a vă conecta oricum"</string> + <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Atinge pentru a te conecta oricum"</string> <string name="network_switch_metered" msgid="1531869544142283384">"S-a comutat la <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string> <string name="network_switch_metered_detail" msgid="1358296010128405906">"Dispozitivul folosește <xliff:g id="NEW_NETWORK">%1$s</xliff:g> când <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nu are acces la internet. Se pot aplica taxe."</string> <string name="network_switch_metered_toast" msgid="501662047275723743">"S-a comutat de la <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> la <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string> @@ -1316,37 +1316,37 @@ <item msgid="9177085807664964627">"VPN"</item> </string-array> <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"un tip de rețea necunoscut"</string> - <string name="accept" msgid="5447154347815825107">"Acceptați"</string> + <string name="accept" msgid="5447154347815825107">"Accept"</string> <string name="decline" msgid="6490507610282145874">"Refuz"</string> <string name="select_character" msgid="3352797107930786979">"Introdu caracterul"</string> <string name="sms_control_title" msgid="4748684259903148341">"Se trimit mesaje SMS"</string> - <string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> trimite un număr mare de mesaje SMS. Permiteți acestei aplicații să trimită în continuare mesaje?"</string> - <string name="sms_control_yes" msgid="4858845109269524622">"Permiteți"</string> + <string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> trimite un număr mare de mesaje SMS. Permiți acestei aplicații să trimită în continuare mesaje?"</string> + <string name="sms_control_yes" msgid="4858845109269524622">"Permite"</string> <string name="sms_control_no" msgid="4845717880040355570">"Refuz"</string> <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> intenționează să trimită un mesaj la <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>."</string> - <string name="sms_short_code_details" msgid="2723725738333388351">"Acest lucru "<b>"poate genera costuri"</b>" în contul dvs. mobil."</string> - <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Acest lucru va genera costuri în contul dvs. mobil."</b></string> + <string name="sms_short_code_details" msgid="2723725738333388351">"Acest lucru "<b>"poate genera costuri"</b>" în contul tău mobil."</string> + <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Acest lucru va genera costuri în contul tău mobil."</b></string> <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"Trimite"</string> <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"Anulează"</string> - <string name="sms_short_code_remember_choice" msgid="1374526438647744862">"Doresc să se rețină opțiunea"</string> - <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Puteți modifica ulterior în Setări > Aplicații"</string> - <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Permiteți întotdeauna"</string> - <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Nu permiteți niciodată"</string> + <string name="sms_short_code_remember_choice" msgid="1374526438647744862">"Reține opțiunea"</string> + <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Poți modifica ulterior în Setări > Aplicații"</string> + <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Permite întotdeauna"</string> + <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Nu permite niciodată"</string> <string name="sim_removed_title" msgid="5387212933992546283">"Card SIM eliminat"</string> - <string name="sim_removed_message" msgid="9051174064474904617">"Rețeaua mobilă va fi indisponibilă până când reporniți cu un card SIM valid introdus."</string> + <string name="sim_removed_message" msgid="9051174064474904617">"Rețeaua mobilă va fi indisponibilă până când repornești cu un card SIM valid introdus."</string> <string name="sim_done_button" msgid="6464250841528410598">"Terminat"</string> <string name="sim_added_title" msgid="7930779986759414595">"Card SIM adăugat"</string> - <string name="sim_added_message" msgid="6602906609509958680">"Reporniți dispozitivul pentru a accesa rețeaua mobilă."</string> - <string name="sim_restart_button" msgid="8481803851341190038">"Reporniți"</string> - <string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Activați serviciul mobil"</string> - <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Descărcați aplicația operatorului pentru a vă activa noul card SIM"</string> - <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Descărcați aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> pentru a vă activa noul card SIM"</string> - <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Descărcați aplicația"</string> + <string name="sim_added_message" msgid="6602906609509958680">"Repornește dispozitivul pentru a accesa rețeaua mobilă."</string> + <string name="sim_restart_button" msgid="8481803851341190038">"Repornește"</string> + <string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Activează serviciul mobil"</string> + <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Descarcă aplicația operatorului pentru a activa noul card SIM"</string> + <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Descarcă aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> pentru a-ți activa noul card SIM"</string> + <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Descarcă aplicația"</string> <string name="carrier_app_notification_title" msgid="5815477368072060250">"S-a introdus un card SIM nou"</string> - <string name="carrier_app_notification_text" msgid="6567057546341958637">"Atingeți pentru a-l configura"</string> - <string name="time_picker_dialog_title" msgid="9053376764985220821">"Setați ora"</string> - <string name="date_picker_dialog_title" msgid="5030520449243071926">"Setați data"</string> - <string name="date_time_set" msgid="4603445265164486816">"Setați"</string> + <string name="carrier_app_notification_text" msgid="6567057546341958637">"Atinge pentru a-l configura"</string> + <string name="time_picker_dialog_title" msgid="9053376764985220821">"Setează ora"</string> + <string name="date_picker_dialog_title" msgid="5030520449243071926">"Setează data"</string> + <string name="date_time_set" msgid="4603445265164486816">"Setează"</string> <string name="date_time_done" msgid="8363155889402873463">"Terminat"</string> <string name="perms_new_perm_prefix" msgid="6984556020395757087"><font size="12" fgcolor="#ff33b5e5">"NOU: "</font></string> <string name="perms_description_app" msgid="2747752389870161996">"Furnizată de <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> @@ -1360,82 +1360,82 @@ <string name="usb_tether_notification_title" msgid="8828527870612663771">"Tetheringul prin USB este activat"</string> <string name="usb_midi_notification_title" msgid="7404506788950595557">"MIDI prin USB este activat"</string> <string name="usb_accessory_notification_title" msgid="1385394660861956980">"Accesoriu USB conectat"</string> - <string name="usb_notification_message" msgid="4715163067192110676">"Atingeți pentru mai multe opțiuni."</string> - <string name="usb_power_notification_message" msgid="7284765627437897702">"Se încarcă dispozitivul conectat. Atingeți pentru mai multe opțiuni."</string> + <string name="usb_notification_message" msgid="4715163067192110676">"Atinge pentru mai multe opțiuni."</string> + <string name="usb_power_notification_message" msgid="7284765627437897702">"Se încarcă dispozitivul conectat. Atinge pentru mai multe opțiuni."</string> <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"S-a detectat un accesoriu audio analogic"</string> - <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Dispozitivul atașat nu este compatibil cu acest telefon. Atingeți pentru a afla mai multe."</string> + <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Dispozitivul atașat nu este compatibil cu acest telefon. Atinge pentru a afla mai multe."</string> <string name="adb_active_notification_title" msgid="408390247354560331">"Remedierea erorilor prin USB conectată"</string> - <string name="adb_active_notification_message" msgid="5617264033476778211">"Atingeți pentru a dezactiva."</string> - <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Selectați pentru a dezactiva remedierea erorilor prin USB."</string> + <string name="adb_active_notification_message" msgid="5617264033476778211">"Atinge pentru a dezactiva."</string> + <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Selectează pentru a dezactiva remedierea erorilor prin USB."</string> <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Remedierea erorilor wireless este activă"</string> <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Atinge pentru a dezactiva remedierea erorilor wireless"</string> - <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Selectați pentru a dezactiva remedierea erorilor wireless."</string> + <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Selectează pentru a dezactiva remedierea erorilor wireless."</string> <string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Modul Set de testare este activat"</string> - <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Reveniți la setările din fabrică pentru a dezactiva modul Set de testare."</string> + <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Revino la setările din fabrică pentru a dezactiva modul Set de testare."</string> <string name="console_running_notification_title" msgid="6087888939261635904">"Consola din serie este activată"</string> - <string name="console_running_notification_message" msgid="7892751888125174039">"Performanța este afectată. Pentru a dezactiva, verificați programul bootloader."</string> + <string name="console_running_notification_message" msgid="7892751888125174039">"Performanța este afectată. Pentru a dezactiva, verifică programul bootloader."</string> <string name="mte_override_notification_title" msgid="4731115381962792944">"MTE experimentală activată"</string> - <string name="mte_override_notification_message" msgid="2441170442725738942">"Performanța și stabilitatea pot fi afectate. Reporniți pentru a dezactiva. Dacă s-a activat cu arm64.memtag.bootctl, setați înainte la niciuna."</string> + <string name="mte_override_notification_message" msgid="2441170442725738942">"Performanța și stabilitatea pot fi afectate. Repornește pentru a dezactiva. Dacă s-a activat cu arm64.memtag.bootctl, setează dinainte la niciuna."</string> <string name="usb_contaminant_detected_title" msgid="4359048603069159678">"Lichide sau reziduuri în portul USB"</string> - <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"Portul USB este dezactivat automat. Atingeți ca să aflați mai multe."</string> + <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"Portul USB este dezactivat automat. Atinge ca să afli mai multe."</string> <string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"Portul USB poate fi folosit"</string> <string name="usb_contaminant_not_detected_message" msgid="892863190942660462">"Telefonul nu mai detectează lichide sau reziduuri."</string> <string name="taking_remote_bugreport_notification_title" msgid="1582531382166919850">"Se creează un raport de eroare…"</string> <string name="share_remote_bugreport_notification_title" msgid="6708897723753334999">"Trimiți raportul de eroare?"</string> <string name="sharing_remote_bugreport_notification_title" msgid="3077385149217638550">"Se trimite raportul de eroare…"</string> - <string name="share_remote_bugreport_notification_message_finished" msgid="7325635795739260135">"Administratorul dvs. a solicitat un raport de eroare pentru a remedia problemele acestui dispozitiv. Este posibil să se permită accesul la date și aplicații."</string> - <string name="share_remote_bugreport_action" msgid="7630880678785123682">"TRIMITEȚI"</string> - <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"REFUZAȚI"</string> + <string name="share_remote_bugreport_notification_message_finished" msgid="7325635795739260135">"Administratorul a solicitat un raport de eroare pentru a remedia problemele acestui dispozitiv. E posibil să se permită accesul la date și aplicații."</string> + <string name="share_remote_bugreport_action" msgid="7630880678785123682">"TRIMITE"</string> + <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"REFUZ"</string> <string name="select_input_method" msgid="3971267998568587025">"Alege metoda de introducere de text"</string> <string name="show_ime" msgid="6406112007347443383">"Se păstrează pe ecran cât timp este activată tastatura fizică"</string> - <string name="hardware" msgid="1800597768237606953">"Afișați tastatura virtuală"</string> + <string name="hardware" msgid="1800597768237606953">"Afișează tastatura virtuală"</string> <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"Configurează tastatura fizică"</string> - <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Atingeți pentru a selecta limba și aspectul"</string> + <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Atinge pentru a selecta limba și aspectul"</string> <string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"Afișare peste alte aplicații"</string> <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> se afișează peste alte aplicații"</string> <string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> se afișează peste aplicații"</string> - <string name="alert_windows_notification_message" msgid="6538171456970725333">"Dacă nu doriți ca <xliff:g id="NAME">%s</xliff:g> să utilizeze această funcție, atingeți pentru a deschide setările și dezactivați-o."</string> - <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Dezactivați"</string> + <string name="alert_windows_notification_message" msgid="6538171456970725333">"Dacă nu vrei ca <xliff:g id="NAME">%s</xliff:g> să folosească această funcție, atinge pentru a deschide setările și dezactiveaz-o."</string> + <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Dezactivează"</string> <string name="ext_media_checking_notification_title" msgid="8299199995416510094">"Se verifică <xliff:g id="NAME">%s</xliff:g>…"</string> <string name="ext_media_checking_notification_message" msgid="2231566971425375542">"Se examinează conținutul curent"</string> <string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"Se analizează spațiul de stocare media"</string> <string name="ext_media_new_notification_title" msgid="3517407571407687677">"<xliff:g id="NAME">%s</xliff:g> nou"</string> <string name="ext_media_new_notification_title" product="automotive" msgid="9085349544984742727">"<xliff:g id="NAME">%s</xliff:g> nu funcționează"</string> - <string name="ext_media_new_notification_message" msgid="6095403121990786986">"Atingeți pentru a configura"</string> - <string name="ext_media_new_notification_message" product="tv" msgid="216863352100263668">"Selectați pentru a configura"</string> - <string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"Poate fi nevoie să reformatați dispozitivul. Atingeți pentru a-l scoate."</string> + <string name="ext_media_new_notification_message" msgid="6095403121990786986">"Atinge pentru a configura"</string> + <string name="ext_media_new_notification_message" product="tv" msgid="216863352100263668">"Selectează pentru a configura"</string> + <string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"Poate fi nevoie să reformatezi dispozitivul. Atinge pentru a-l scoate."</string> <string name="ext_media_ready_notification_message" msgid="7509496364380197369">"Pentru stocarea de fotografii, videoclipuri, muzică și altele"</string> - <string name="ext_media_ready_notification_message" product="tv" msgid="8847134811163165935">"Răsfoiți fișierele media"</string> + <string name="ext_media_ready_notification_message" product="tv" msgid="8847134811163165935">"Răsfoiește fișierele media"</string> <string name="ext_media_unmountable_notification_title" msgid="4895444667278979910">"Problemă cu <xliff:g id="NAME">%s</xliff:g>"</string> <string name="ext_media_unmountable_notification_title" product="automotive" msgid="3142723758949023280">"<xliff:g id="NAME">%s</xliff:g> nu funcționează"</string> - <string name="ext_media_unmountable_notification_message" msgid="3256290114063126205">"Atingeți pentru a remedia"</string> - <string name="ext_media_unmountable_notification_message" product="tv" msgid="3003611129979934633">"<xliff:g id="NAME">%s</xliff:g> este corupt. Selectați pentru a remedia."</string> - <string name="ext_media_unmountable_notification_message" product="automotive" msgid="2274596120715020680">"Poate fi nevoie să reformatați dispozitivul. Atingeți pentru a-l scoate."</string> + <string name="ext_media_unmountable_notification_message" msgid="3256290114063126205">"Atinge pentru a remedia"</string> + <string name="ext_media_unmountable_notification_message" product="tv" msgid="3003611129979934633">"<xliff:g id="NAME">%s</xliff:g> este corupt. Selectează pentru a remedia."</string> + <string name="ext_media_unmountable_notification_message" product="automotive" msgid="2274596120715020680">"Poate fi nevoie să reformatezi dispozitivul. Atinge pentru a-l scoate."</string> <string name="ext_media_unsupported_notification_title" msgid="3487534182861251401">"S-a detectat <xliff:g id="NAME">%s</xliff:g>"</string> <string name="ext_media_unsupported_notification_title" product="automotive" msgid="6004193172658722381">"<xliff:g id="NAME">%s</xliff:g> nu funcționează"</string> <string name="ext_media_unsupported_notification_message" msgid="8463636521459807981">"Atinge pentru a configura"</string> - <string name="ext_media_unsupported_notification_message" product="tv" msgid="1595482802187036532">"Selectați pentru a configura <xliff:g id="NAME">%s</xliff:g> într-un format acceptat."</string> - <string name="ext_media_unsupported_notification_message" product="automotive" msgid="3412494732736336330">"Poate fi nevoie să reformatați dispozitivul"</string> + <string name="ext_media_unsupported_notification_message" product="tv" msgid="1595482802187036532">"Selectează pentru a configura <xliff:g id="NAME">%s</xliff:g> într-un format acceptat."</string> + <string name="ext_media_unsupported_notification_message" product="automotive" msgid="3412494732736336330">"Poate fi nevoie să reformatezi dispozitivul"</string> <string name="ext_media_badremoval_notification_title" msgid="4114625551266196872">"<xliff:g id="NAME">%s</xliff:g> scos pe neașteptate"</string> <string name="ext_media_badremoval_notification_message" msgid="1986514704499809244">"Deconectează din setări dispozitivele media înainte de a le îndepărta, pentru a evita pierderea conținutului"</string> <string name="ext_media_nomedia_notification_title" msgid="742671636376975890">"S-a eliminat <xliff:g id="NAME">%s</xliff:g>"</string> <string name="ext_media_nomedia_notification_message" msgid="2832724384636625852">"Funcționarea ar putea fi necorespunzătoare. Introdu un dispozitiv de stocare nou."</string> <string name="ext_media_unmounting_notification_title" msgid="4147986383917892162">"Se deconectează <xliff:g id="NAME">%s</xliff:g>"</string> - <string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"Nu scoateți"</string> + <string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"Nu scoate"</string> <string name="ext_media_init_action" msgid="2312974060585056709">"Configurează"</string> - <string name="ext_media_unmount_action" msgid="966992232088442745">"Scoateți"</string> - <string name="ext_media_browse_action" msgid="344865351947079139">"Explorați"</string> - <string name="ext_media_seamless_action" msgid="8837030226009268080">"Schimbați ieșirea"</string> + <string name="ext_media_unmount_action" msgid="966992232088442745">"Scoate"</string> + <string name="ext_media_browse_action" msgid="344865351947079139">"Explorează"</string> + <string name="ext_media_seamless_action" msgid="8837030226009268080">"Schimbă ieșirea"</string> <string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> lipsește"</string> - <string name="ext_media_missing_message" msgid="4408988706227922909">"Reintroduceți dispozitivul"</string> + <string name="ext_media_missing_message" msgid="4408988706227922909">"Reintrodu dispozitivul"</string> <string name="ext_media_move_specific_title" msgid="8492118544775964250">"Se mută <xliff:g id="NAME">%s</xliff:g>"</string> <string name="ext_media_move_title" msgid="2682741525619033637">"Se mută datele"</string> <string name="ext_media_move_success_title" msgid="4901763082647316767">"Transfer de conținut încheiat"</string> <string name="ext_media_move_success_message" msgid="9159542002276982979">"Conținut mutat pe <xliff:g id="NAME">%s</xliff:g>"</string> <string name="ext_media_move_failure_title" msgid="3184577479181333665">"Nu s-a putut muta conținutul"</string> - <string name="ext_media_move_failure_message" msgid="4197306718121869335">"Încercați să mutați din nou conținutul"</string> + <string name="ext_media_move_failure_message" msgid="4197306718121869335">"Încearcă să muți din nou conținutul"</string> <string name="ext_media_status_removed" msgid="241223931135751691">"Eliminat"</string> <string name="ext_media_status_unmounted" msgid="8145812017295835941">"Scos"</string> <string name="ext_media_status_checking" msgid="159013362442090347">"Se verifică..."</string> @@ -1460,61 +1460,61 @@ <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite unei aplicații să solicite permisiunea de a ignora optimizările bateriei pentru aplicația respectivă."</string> <string name="permlab_queryAllPackages" msgid="2928450604653281650">"să interogheze toate pachetele"</string> <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite unei aplicații să vadă toate pachetele instalate."</string> - <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Apăsați de două ori pentru a controla mărirea/micșorarea"</string> + <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Apasă de două ori pentru a controla mărirea/micșorarea"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nu s-a putut adăuga widgetul."</string> - <string name="ime_action_go" msgid="5536744546326495436">"Accesați"</string> - <string name="ime_action_search" msgid="4501435960587287668">"Căutați"</string> + <string name="ime_action_go" msgid="5536744546326495436">"Accesează"</string> + <string name="ime_action_search" msgid="4501435960587287668">"Caută"</string> <string name="ime_action_send" msgid="8456843745664334138">"Trimite"</string> <string name="ime_action_next" msgid="4169702997635728543">"Înainte"</string> <string name="ime_action_done" msgid="6299921014822891569">"Terminat"</string> <string name="ime_action_previous" msgid="6548799326860401611">"Înapoi"</string> - <string name="ime_action_default" msgid="8265027027659800121">"Executați"</string> - <string name="dial_number_using" msgid="6060769078933953531">"Formați numărul\nutilizând <xliff:g id="NUMBER">%s</xliff:g>"</string> - <string name="create_contact_using" msgid="6200708808003692594">"Creați contactul\nutilizând <xliff:g id="NUMBER">%s</xliff:g>"</string> - <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"Următoarele aplicații solicită permisiunea de a accesa contul dvs. acum și în viitor."</string> - <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"Permiteți această solicitare?"</string> + <string name="ime_action_default" msgid="8265027027659800121">"Execută"</string> + <string name="dial_number_using" msgid="6060769078933953531">"Formează numărul\nfolosind <xliff:g id="NUMBER">%s</xliff:g>"</string> + <string name="create_contact_using" msgid="6200708808003692594">"Creează contactul\nutilizând <xliff:g id="NUMBER">%s</xliff:g>"</string> + <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"Următoarele aplicații solicită permisiunea de a-ți accesa contul acum și în viitor."</string> + <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"Permiți această solicitare?"</string> <string name="grant_permissions_header_text" msgid="3420736827804657201">"Solicitare de acces"</string> - <string name="allow" msgid="6195617008611933762">"Permiteți"</string> + <string name="allow" msgid="6195617008611933762">"Permite"</string> <string name="deny" msgid="6632259981847676572">"Refuz"</string> <string name="permission_request_notification_title" msgid="1810025922441048273">"Permisiune solicitată"</string> <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"Permisiune solicitată\npentru contul <xliff:g id="ACCOUNT">%s</xliff:g>."</string> <string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"Permisiune solicitată de <xliff:g id="APP">%1$s</xliff:g>\npentru contul <xliff:g id="ACCOUNT">%2$s</xliff:g>."</string> - <string name="forward_intent_to_owner" msgid="4620359037192871015">"Utilizați această aplicație în afara profilului de serviciu"</string> - <string name="forward_intent_to_work" msgid="3620262405636021151">"Utilizați această aplicație în profilul de serviciu"</string> + <string name="forward_intent_to_owner" msgid="4620359037192871015">"Folosești această aplicație în afara profilului de serviciu"</string> + <string name="forward_intent_to_work" msgid="3620262405636021151">"Folosești această aplicație în profilul de serviciu"</string> <string name="input_method_binding_label" msgid="1166731601721983656">"Metodă de intrare"</string> <string name="sync_binding_label" msgid="469249309424662147">"Sincronizare"</string> <string name="accessibility_binding_label" msgid="1974602776545801715">"Accesibilitate"</string> <string name="wallpaper_binding_label" msgid="1197440498000786738">"Imagine de fundal"</string> - <string name="chooser_wallpaper" msgid="3082405680079923708">"Schimbați imaginea de fundal"</string> + <string name="chooser_wallpaper" msgid="3082405680079923708">"Schimbă imaginea de fundal"</string> <string name="notification_listener_binding_label" msgid="2702165274471499713">"Serviciu de citire a notificărilor"</string> <string name="vr_listener_binding_label" msgid="8013112996671206429">"Instrument de ascultare pentru RV"</string> <string name="condition_provider_service_binding_label" msgid="8490641013951857673">"Furnizor de condiții"</string> <string name="notification_ranker_binding_label" msgid="432708245635563763">"Serviciul de clasificare a notificărilor"</string> <string name="vpn_title" msgid="5906991595291514182">"VPN activat"</string> <string name="vpn_title_long" msgid="6834144390504619998">"VPN este activată de <xliff:g id="APP">%s</xliff:g>"</string> - <string name="vpn_text" msgid="2275388920267251078">"Apăsați pentru a gestiona rețeaua."</string> - <string name="vpn_text_long" msgid="278540576806169831">"Conectat la <xliff:g id="SESSION">%s</xliff:g>. Apăsați pentru a gestiona rețeaua."</string> + <string name="vpn_text" msgid="2275388920267251078">"Apasă pentru a gestiona rețeaua."</string> + <string name="vpn_text_long" msgid="278540576806169831">"Conectat la <xliff:g id="SESSION">%s</xliff:g>. Apasă pentru a gestiona rețeaua."</string> <string name="vpn_lockdown_connecting" msgid="6096725311950342607">"Se efectuează conectarea la rețeaua VPN activată permanent…"</string> <string name="vpn_lockdown_connected" msgid="2853127976590658469">"Conectat(ă) la rețeaua VPN activată permanent"</string> <string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"Deconectat de la rețeaua VPN activată permanent"</string> <string name="vpn_lockdown_error" msgid="4453048646854247947">"Nu s-a putut conecta la rețeaua VPN activată permanent"</string> - <string name="vpn_lockdown_config" msgid="8331697329868252169">"Modificați setările de rețea sau VPN"</string> + <string name="vpn_lockdown_config" msgid="8331697329868252169">"Modifică setările de rețea sau VPN"</string> <string name="upload_file" msgid="8651942222301634271">"Alege un fișier"</string> <string name="no_file_chosen" msgid="4146295695162318057">"Nu au fost găsite fișiere"</string> - <string name="reset" msgid="3865826612628171429">"Resetați"</string> + <string name="reset" msgid="3865826612628171429">"Resetează"</string> <string name="submit" msgid="862795280643405865">"Trimite"</string> <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplicația pentru condus rulează"</string> - <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Atingeți ca să ieșiți din aplicația pentru condus."</string> + <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Atinge ca să ieși din aplicația pentru condus."</string> <string name="back_button_label" msgid="4078224038025043387">"Înapoi"</string> <string name="next_button_label" msgid="6040209156399907780">"Înainte"</string> - <string name="skip_button_label" msgid="3566599811326688389">"Omiteți"</string> + <string name="skip_button_label" msgid="3566599811326688389">"Omite"</string> <string name="no_matches" msgid="6472699895759164599">"Nicio potrivire"</string> - <string name="find_on_page" msgid="5400537367077438198">"Găsiți pe pagină"</string> + <string name="find_on_page" msgid="5400537367077438198">"Caută în pagină"</string> <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# potrivire}few{# din {total}}other{# din {total}}}"</string> <string name="action_mode_done" msgid="2536182504764803222">"Terminat"</string> <string name="progress_erasing" msgid="6891435992721028004">"Se șterge spațiul de stocare distribuit..."</string> - <string name="share" msgid="4157615043345227321">"Distribuiți"</string> - <string name="find" msgid="5015737188624767706">"Găsiți"</string> + <string name="share" msgid="4157615043345227321">"Distribuie"</string> + <string name="find" msgid="5015737188624767706">"Caută"</string> <string name="websearch" msgid="5624340204512793290">"Căutare pe web"</string> <string name="find_next" msgid="5341217051549648153">"Următorul rezultat"</string> <string name="find_previous" msgid="4405898398141275532">"Rezultatul anterior"</string> @@ -1524,46 +1524,46 @@ <string name="gpsVerifYes" msgid="3719843080744112940">"Da"</string> <string name="gpsVerifNo" msgid="1671201856091564741">"Nu"</string> <string name="sync_too_many_deletes" msgid="6999440774578705300">"Limita pentru ștergere a fost depășită"</string> - <string name="sync_too_many_deletes_desc" msgid="7409327940303504440">"Există <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> elemente șterse pentru <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, contul <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. Ce doriți să faceți?"</string> - <string name="sync_really_delete" msgid="5657871730315579051">"Ștergeți elementele"</string> + <string name="sync_too_many_deletes_desc" msgid="7409327940303504440">"Există <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> elemente șterse pentru <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, contul <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. Ce vrei să faci?"</string> + <string name="sync_really_delete" msgid="5657871730315579051">"Șterge elementele"</string> <string name="sync_undo_deletes" msgid="5786033331266418896">"Anulează aceste ștergeri"</string> - <string name="sync_do_nothing" msgid="4528734662446469646">"Nu trebuie să luați nicio măsură deocamdată"</string> + <string name="sync_do_nothing" msgid="4528734662446469646">"Nu trebuie să iei nicio măsură deocamdată"</string> <string name="choose_account_label" msgid="5557833752759831548">"Alege un cont"</string> - <string name="add_account_label" msgid="4067610644298737417">"Adăugați un cont"</string> - <string name="add_account_button_label" msgid="322390749416414097">"Adăugați un cont"</string> - <string name="number_picker_increment_button" msgid="7621013714795186298">"Creșteți"</string> - <string name="number_picker_decrement_button" msgid="5116948444762708204">"Reduceți"</string> - <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> atingeți lung."</string> - <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Glisați în sus pentru a crește și în jos pentru a reduce."</string> - <string name="time_picker_increment_minute_button" msgid="7195870222945784300">"Creșteți valoarea pentru minute"</string> - <string name="time_picker_decrement_minute_button" msgid="230925389943411490">"Reduceți valoarea pentru minute"</string> - <string name="time_picker_increment_hour_button" msgid="3063572723197178242">"Creșteți valoarea pentru oră"</string> - <string name="time_picker_decrement_hour_button" msgid="584101766855054412">"Reduceți valoarea pentru oră"</string> - <string name="time_picker_increment_set_pm_button" msgid="5889149366900376419">"Setați valoarea PM"</string> - <string name="time_picker_decrement_set_am_button" msgid="1422608001541064087">"Setați valoarea AM"</string> - <string name="date_picker_increment_month_button" msgid="3447263316096060309">"Creșteți valoarea pentru lună"</string> - <string name="date_picker_decrement_month_button" msgid="6531888937036883014">"Reduceți valoarea pentru lună"</string> - <string name="date_picker_increment_day_button" msgid="4349336637188534259">"Creșteți valoarea pentru zi"</string> - <string name="date_picker_decrement_day_button" msgid="6840253837656637248">"Reduceți valoarea pentru zi"</string> - <string name="date_picker_increment_year_button" msgid="7608128783435372594">"Creșteți valoarea pentru an"</string> - <string name="date_picker_decrement_year_button" msgid="4102586521754172684">"Reduceți valoarea pentru an"</string> + <string name="add_account_label" msgid="4067610644298737417">"Adaugă un cont"</string> + <string name="add_account_button_label" msgid="322390749416414097">"Adaugă un cont"</string> + <string name="number_picker_increment_button" msgid="7621013714795186298">"Mărește"</string> + <string name="number_picker_decrement_button" msgid="5116948444762708204">"Redu"</string> + <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> atinge lung."</string> + <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Glisează în sus pentru a crește și în jos pentru a reduce."</string> + <string name="time_picker_increment_minute_button" msgid="7195870222945784300">"Mărește valoarea pentru minute"</string> + <string name="time_picker_decrement_minute_button" msgid="230925389943411490">"Redu valoarea pentru minute"</string> + <string name="time_picker_increment_hour_button" msgid="3063572723197178242">"Mărește valoarea pentru oră"</string> + <string name="time_picker_decrement_hour_button" msgid="584101766855054412">"Redu valoarea pentru oră"</string> + <string name="time_picker_increment_set_pm_button" msgid="5889149366900376419">"Setează valoarea PM"</string> + <string name="time_picker_decrement_set_am_button" msgid="1422608001541064087">"Setează valoarea AM"</string> + <string name="date_picker_increment_month_button" msgid="3447263316096060309">"Mărește valoarea pentru lună"</string> + <string name="date_picker_decrement_month_button" msgid="6531888937036883014">"Redu valoarea pentru lună"</string> + <string name="date_picker_increment_day_button" msgid="4349336637188534259">"Mărește valoarea pentru zi"</string> + <string name="date_picker_decrement_day_button" msgid="6840253837656637248">"Redu valoarea pentru zi"</string> + <string name="date_picker_increment_year_button" msgid="7608128783435372594">"Mărește valoarea pentru an"</string> + <string name="date_picker_decrement_year_button" msgid="4102586521754172684">"Redu valoarea pentru an"</string> <string name="date_picker_prev_month_button" msgid="3418694374017868369">"Luna trecută"</string> <string name="date_picker_next_month_button" msgid="4858207337779144840">"Luna viitoare"</string> <string name="keyboardview_keycode_alt" msgid="8997420058584292385">"Alt"</string> <string name="keyboardview_keycode_cancel" msgid="2134624484115716975">"Anulează"</string> - <string name="keyboardview_keycode_delete" msgid="2661117313730098650">"Ștergeți"</string> + <string name="keyboardview_keycode_delete" msgid="2661117313730098650">"Șterge"</string> <string name="keyboardview_keycode_done" msgid="2524518019001653851">"Terminat"</string> <string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"Schimbarea modului"</string> <string name="keyboardview_keycode_shift" msgid="3026509237043975573">"Shift"</string> <string name="keyboardview_keycode_enter" msgid="168054869339091055">"Enter"</string> <string name="activitychooserview_choose_application" msgid="3500574466367891463">"Alege o aplicație"</string> <string name="activitychooserview_choose_application_error" msgid="6937782107559241734">"Nu s-a putut lansa <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> - <string name="shareactionprovider_share_with" msgid="2753089758467748982">"Permiteți accesul pentru"</string> - <string name="shareactionprovider_share_with_application" msgid="4902832247173666973">"Permiteți accesul pentru <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> - <string name="content_description_sliding_handle" msgid="982510275422590757">"Mâner glisant. Atingeți și țineți apăsat."</string> - <string name="description_target_unlock_tablet" msgid="7431571180065859551">"Glisați pentru a debloca."</string> - <string name="action_bar_home_description" msgid="1501655419158631974">"Navigați la ecranul de pornire"</string> - <string name="action_bar_up_description" msgid="6611579697195026932">"Navigați în sus"</string> + <string name="shareactionprovider_share_with" msgid="2753089758467748982">"Permite accesul pentru"</string> + <string name="shareactionprovider_share_with_application" msgid="4902832247173666973">"Permite accesul pentru <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> + <string name="content_description_sliding_handle" msgid="982510275422590757">"Ghidaj glisant. Atinge și ține apăsat."</string> + <string name="description_target_unlock_tablet" msgid="7431571180065859551">"Glisează pentru a debloca."</string> + <string name="action_bar_home_description" msgid="1501655419158631974">"Navighează la ecranul de pornire"</string> + <string name="action_bar_up_description" msgid="6611579697195026932">"Navighează în sus"</string> <string name="action_menu_overflow_description" msgid="4579536843510088170">"Mai multe opțiuni"</string> <string name="action_bar_home_description_format" msgid="5087107531331621803">"%1$s, %2$s"</string> <string name="action_bar_home_subtitle_description_format" msgid="4346835454749569826">"%1$s, %2$s, %3$s"</string> @@ -1573,19 +1573,19 @@ <string name="storage_usb_drive" msgid="448030813201444573">"Unitate USB"</string> <string name="storage_usb_drive_label" msgid="6631740655876540521">"Unitate USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string> <string name="storage_usb" msgid="2391213347883616886">"Dsipozitiv de stocare USB"</string> - <string name="extract_edit_menu_button" msgid="63954536535863040">"Editați"</string> + <string name="extract_edit_menu_button" msgid="63954536535863040">"Editează"</string> <string name="data_usage_warning_title" msgid="9034893717078325845">"Avertisment pentru date"</string> - <string name="data_usage_warning_body" msgid="1669325367188029454">"Ați folosit <xliff:g id="APP">%s</xliff:g> din date"</string> + <string name="data_usage_warning_body" msgid="1669325367188029454">"Ai folosit <xliff:g id="APP">%s</xliff:g> din date"</string> <string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"S-a atins limita de date mobile"</string> - <string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Ați atins limita de date Wi-Fi"</string> + <string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Ai atins limita de date Wi-Fi"</string> <string name="data_usage_limit_body" msgid="3567699582000085710">"Datele au fost întrerupte pentru restul ciclului"</string> <string name="data_usage_mobile_limit_snoozed_title" msgid="101888478915677895">"Peste limita de date mobile"</string> <string name="data_usage_wifi_limit_snoozed_title" msgid="1622359254521960508">"Peste limita de date Wi-Fi"</string> - <string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"Ați depășit limita stabilită cu <xliff:g id="SIZE">%s</xliff:g>"</string> + <string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"Ai depășit limita stabilită cu <xliff:g id="SIZE">%s</xliff:g>"</string> <string name="data_usage_restricted_title" msgid="126711424380051268">"Datele de fundal restricționate"</string> - <string name="data_usage_restricted_body" msgid="5338694433686077733">"Atingeți ca să eliminați restricția."</string> + <string name="data_usage_restricted_body" msgid="5338694433686077733">"Atinge ca să elimini restricția."</string> <string name="data_usage_rapid_title" msgid="2950192123248740375">"Utilizare mare de date mobile"</string> - <string name="data_usage_rapid_body" msgid="3886676853263693432">"Aplicațiile dvs. au utilizat mai multe date decât de obicei"</string> + <string name="data_usage_rapid_body" msgid="3886676853263693432">"Aplicațiile au folosit mai multe date decât de obicei"</string> <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> a utilizat mai multe date decât de obicei"</string> <string name="ssl_certificate" msgid="5690020361307261997">"Certificat de securitate"</string> <string name="ssl_certificate_is_valid" msgid="7293675884598527081">"Certificatul este valid."</string> @@ -1601,11 +1601,11 @@ <string name="fingerprints" msgid="148690767172613723">"Amprente:"</string> <string name="sha256_fingerprint" msgid="7103976380961964600">"Amprentă SHA-256:"</string> <string name="sha1_fingerprint" msgid="2339915142825390774">"Amprentă SHA-1:"</string> - <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Afișați-le pe toate"</string> + <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Afișează-le pe toate"</string> <string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"Alege activitatea"</string> - <string name="share_action_provider_share_with" msgid="1904096863622941880">"Distribuiți pentru"</string> + <string name="share_action_provider_share_with" msgid="1904096863622941880">"Distribuie pentru"</string> <string name="sending" msgid="206925243621664438">"Se trimite..."</string> - <string name="launchBrowserDefault" msgid="6328349989932924119">"Lansați browserul?"</string> + <string name="launchBrowserDefault" msgid="6328349989932924119">"Lansezi browserul?"</string> <string name="SetupCallDefault" msgid="5581740063237175247">"Accepți apelul?"</string> <string name="activity_resolver_use_always" msgid="5575222334666843269">"Întotdeauna"</string> <string name="activity_resolver_use_once" msgid="948462794469672658">"Numai o dată"</string> @@ -1621,8 +1621,8 @@ <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"Audio Bluetooth"</string> <string name="wireless_display_route_description" msgid="8297563323032966831">"Ecran wireless"</string> <string name="media_route_button_content_description" msgid="2299223698196869956">"Trimite"</string> - <string name="media_route_chooser_title" msgid="6646594924991269208">"Conectați-vă la dispozitiv"</string> - <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Proiectați ecranul pe dispozitiv"</string> + <string name="media_route_chooser_title" msgid="6646594924991269208">"Conectează-te la dispozitiv"</string> + <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Proiectează ecranul pe dispozitiv"</string> <string name="media_route_chooser_searching" msgid="6119673534251329535">"Se caută dispozitive..."</string> <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Setări"</string> <string name="media_route_controller_disconnect" msgid="7362617572732576959">"Deconectează-te"</string> @@ -1640,82 +1640,82 @@ <string name="kg_wrong_pattern" msgid="1342812634464179931">"Model greșit"</string> <string name="kg_wrong_password" msgid="2384677900494439426">"Parolă greșită"</string> <string name="kg_wrong_pin" msgid="3680925703673166482">"Cod PIN greșit"</string> - <string name="kg_pattern_instructions" msgid="8366024510502517748">"Desenați modelul"</string> + <string name="kg_pattern_instructions" msgid="8366024510502517748">"Desenează modelul"</string> <string name="kg_sim_pin_instructions" msgid="6479401489471690359">"Introdu codul PIN al cardului SIM"</string> <string name="kg_pin_instructions" msgid="7355933174673539021">"Introdu codul PIN"</string> <string name="kg_password_instructions" msgid="7179782578809398050">"Introdu parola"</string> - <string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"Cardul SIM este acum dezactivat. Introduceți codul PUK pentru a continua. Contactați operatorul pentru mai multe detalii."</string> + <string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"Cardul SIM este acum dezactivat. Introdu codul PUK pentru a continua. Contactează operatorul pentru mai multe detalii."</string> <string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"Introdu codul PIN dorit"</string> - <string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Confirmați codul PIN dorit"</string> + <string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Confirmă codul PIN dorit"</string> <string name="kg_sim_unlock_progress_dialog_message" msgid="8871937892678885545">"Se deblochează cardul SIM..."</string> <string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"Cod PIN incorect."</string> <string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"Introdu un cod PIN format din 4 până la 8 cifre."</string> <string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"Codul PUK trebuie să conțină 8 numere."</string> - <string name="kg_invalid_puk" msgid="4809502818518963344">"Reintroduceți codul PUK corect. Încercările repetate vor dezactiva definitiv cardul SIM."</string> + <string name="kg_invalid_puk" msgid="4809502818518963344">"Reintrodu codul PUK corect. Încercările repetate vor dezactiva definitiv cardul SIM."</string> <string name="kg_invalid_confirm_pin_hint" product="default" msgid="4705368340409816254">"Codurile PIN nu coincid"</string> <string name="kg_login_too_many_attempts" msgid="699292728290654121">"Prea multe încercări de desenare a modelului"</string> - <string name="kg_login_instructions" msgid="3619844310339066827">"Pentru a debloca, conectați-vă cu Contul dvs. Google."</string> + <string name="kg_login_instructions" msgid="3619844310339066827">"Pentru a debloca, conectează-te folosind Contul Google."</string> <string name="kg_login_username_hint" msgid="1765453775467133251">"Nume de utilizator (e-mail)"</string> <string name="kg_login_password_hint" msgid="3330530727273164402">"Parolă"</string> <string name="kg_login_submit_button" msgid="893611277617096870">"Conectează-te"</string> <string name="kg_login_invalid_input" msgid="8292367491901220210">"Nume de utilizator sau parolă nevalide."</string> - <string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"Ați uitat numele de utilizator sau parola?\nAccesați "<b>"google.com/accounts/recovery"</b>"."</string> + <string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"Ai uitat numele de utilizator sau parola?\nAccesează "<b>"google.com/accounts/recovery"</b>"."</string> <string name="kg_login_checking_password" msgid="4676010303243317253">"Se verifică contul…"</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Ați introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> - <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"Ați introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> - <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> - <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, aceasta va fi resetată la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string> - <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va reveni la setările din fabrică, iar toate datele de utilizator se vor pierde."</string> - <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va fi resetat la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string> - <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Tableta va fi acum resetată la setările prestabilite din fabrică."</string> - <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. Acesta va reveni la setările din fabrică."</string> - <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Telefonul va fi acum resetat la setările prestabilite din fabrică."</string> - <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați tableta cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> - <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați dispozitivul Android TV cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> - <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Ai introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> + <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"Ai introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> + <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> + <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, aceasta va reveni la setările din fabrică, iar toate datele de utilizator se vor pierde."</string> + <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va reveni la setările din fabrică, iar toate datele de utilizator se vor pierde."</string> + <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va reveni la setările din fabrică, iar toate datele de utilizator se vor pierde."</string> + <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Tableta va reveni acum la setările din fabrică."</string> + <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. Acesta va reveni la setările din fabrică."</string> + <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Telefonul va reveni acum la setările din fabrică."</string> + <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, ți se va solicita să deblochezi tableta cu ajutorul unui cont de e-mail.\n\n Încearcă din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> + <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, ți se va solicita să deblochezi dispozitivul Android TV cu ajutorul unui cont de e-mail.\n\n Încearcă din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> + <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, ți se va solicita să deblochezi telefonul cu ajutorul unui cont de e-mail.\n\n Încearcă din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> <string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string> - <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Eliminați"</string> - <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ridicați volumul mai sus de nivelul recomandat?\n\nAscultarea la volum ridicat pe perioade lungi de timp vă poate afecta auzul."</string> - <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Utilizați comanda rapidă pentru accesibilitate?"</string> - <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Atunci când comanda rapidă este activată, dacă apăsați ambele butoane de volum timp de trei secunde, veți lansa o funcție de accesibilitate."</string> - <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Activați comanda rapidă pentru funcțiile de accesibilitate?"</string> - <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Dacă apăsați ambele taste de volum câteva secunde, activați funcțiile de accesibilitate. Acest lucru poate schimba funcționarea dispozitivului.\n\nFuncțiile actuale:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuteți schimba funcțiile selectate din Setări > Accesibilitate."</string> + <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Elimină"</string> + <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Mărești volumul peste nivelul recomandat?\n\nDacă asculți perioade lungi la volum ridicat, auzul poate fi afectat."</string> + <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Folosești comanda rapidă pentru accesibilitate?"</string> + <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Când comanda rapidă e activată, dacă apeși ambele butoane de volum timp de trei secunde, vei lansa o funcție de accesibilitate."</string> + <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Activezi comanda rapidă pentru funcțiile de accesibilitate?"</string> + <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Dacă apeși ambele taste de volum câteva secunde, activezi funcțiile de accesibilitate. Acest lucru poate schimba funcționarea dispozitivului.\n\nFuncțiile actuale:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPoți schimba funcțiile selectate din Setări > Accesibilitate."</string> <string name="accessibility_shortcut_multiple_service_list" msgid="2128323171922023762">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string> - <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Activați comanda rapidă <xliff:g id="SERVICE">%1$s</xliff:g>?"</string> - <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Dacă apăsați ambele taste de volum câteva secunde, activați funcția de accesibilitate <xliff:g id="SERVICE">%1$s</xliff:g>. Acest lucru poate schimba funcționarea dispozitivului.\n\nPuteți alege altă funcție pentru această comandă în Setări > Accesibilitate."</string> - <string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activați"</string> - <string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nu activați"</string> + <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Activezi comanda rapidă <xliff:g id="SERVICE">%1$s</xliff:g>?"</string> + <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Dacă apeși ambele taste de volum câteva secunde, activezi funcția de accesibilitate <xliff:g id="SERVICE">%1$s</xliff:g>. Acest lucru poate schimba funcționarea dispozitivului.\n\nPoți alege altă funcție pentru această comandă în Setări > Accesibilitate."</string> + <string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activează"</string> + <string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nu activa"</string> <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"ACTIVAT"</string> <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"DEZACTIVAT"</string> - <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Permiteți serviciului <xliff:g id="SERVICE">%1$s</xliff:g> să aibă control total asupra dispozitivului dvs.?"</string> - <string name="accessibility_service_warning_description" msgid="291674995220940133">"Controlul total este adecvat pentru aplicații care vă ajută cu accesibilitatea, însă nu pentru majoritatea aplicaților."</string> - <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Vă vede și vă controlează ecranul"</string> + <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Permiți serviciului <xliff:g id="SERVICE">%1$s</xliff:g> să aibă control total asupra dispozitivului?"</string> + <string name="accessibility_service_warning_description" msgid="291674995220940133">"Controlul total este adecvat pentru aplicații care te ajută cu accesibilitatea, însă nu pentru majoritatea aplicaților."</string> + <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"să vadă și să controleze ecranul"</string> <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Poate citi tot conținutul de pe ecran și poate afișa conținut peste alte aplicații."</string> - <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Vă vede interacțiunile și le realizează"</string> - <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Poate urmări interacțiunile dvs. cu o aplicație sau cu un senzor hardware și poate interacționa cu aplicații în numele dvs."</string> - <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permiteți"</string> + <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"să vadă și să facă acțiuni"</string> + <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Poate să urmărească interacțiunile tale cu o aplicație sau cu un senzor hardware și să interacționeze cu aplicații în numele tău."</string> + <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permite"</string> <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Refuz"</string> - <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Atingeți o funcție ca să începeți să o folosiți:"</string> - <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Alegeți funcțiile pe care să le folosiți cu butonul de accesibilitate"</string> + <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Atinge o funcție ca să începi să o folosești:"</string> + <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Alege funcțiile pe care să le folosești cu butonul de accesibilitate"</string> <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Alege funcțiile pentru comanda rapidă a butonului de volum"</string> <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> a fost dezactivat"</string> - <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editați comenzile rapide"</string> + <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editează comenzile rapide"</string> <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gata"</string> - <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Dezactivați comanda rapidă"</string> - <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizați comanda rapidă"</string> + <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Dezactivează comanda rapidă"</string> + <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Folosește comanda rapidă"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversarea culorilor"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Corecția culorii"</string> <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modul cu o mână"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Luminozitate redusă suplimentar"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S-au apăsat lung tastele de volum. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S-au apăsat lung tastele de volum. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> - <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Apăsați ambele butoane de volum timp de trei secunde pentru a folosi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Alegeți o funcție pe care să o folosiți când atingeți butonul de accesibilitate:"</string> - <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Alegeți o funcție pe care să o folosiți cu gestul de accesibilitate (glisați în sus cu două degete din partea de jos a ecranului):"</string> - <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Alegeți o funcție pe care să o folosiți cu gestul de accesibilitate (glisați în sus cu trei degete din partea de jos a ecranului):"</string> - <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Pentru a comuta între funcții, atingeți lung butonul de accesibilitate."</string> - <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Pentru a comuta între funcții, glisați în sus cu două degete și mențineți apăsat."</string> - <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Pentru a comuta între funcții, glisați în sus cu trei degete și mențineți apăsat."</string> + <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Apasă ambele butoane de volum timp de trei secunde pentru a folosi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> + <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Alege o funcție pe care să o folosești când atingi butonul de accesibilitate:"</string> + <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Alege o funcție pe care să o folosești cu gestul de accesibilitate (glisează în sus cu două degete din partea de jos a ecranului):"</string> + <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Alege o funcție pe care să o folosești cu gestul de accesibilitate (glisează în sus cu trei degete din partea de jos a ecranului):"</string> + <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Pentru a comuta între funcții, atinge lung butonul de accesibilitate."</string> + <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Pentru a comuta între funcții, glisează în sus cu două degete și ține apăsat."</string> + <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Pentru a comuta între funcții, glisează în sus cu trei degete și ține apăsat."</string> <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Mărire"</string> <string name="user_switched" msgid="7249833311585228097">"Utilizator curent: <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="user_switching_message" msgid="1912993630661332336">"Se comută la <xliff:g id="NAME">%1$s</xliff:g>…"</string> @@ -1723,9 +1723,9 @@ <string name="owner_name" msgid="8713560351570795743">"Proprietar"</string> <string name="guest_name" msgid="8502103277839834324">"Invitat"</string> <string name="error_message_title" msgid="4082495589294631966">"Eroare"</string> - <string name="error_message_change_not_allowed" msgid="843159705042381454">"Această modificare nu este permisă de administratorul dvs."</string> + <string name="error_message_change_not_allowed" msgid="843159705042381454">"Această modificare nu este permisă de administrator"</string> <string name="app_not_found" msgid="3429506115332341800">"Nicio aplicație pentru gestionarea acestei acțiuni"</string> - <string name="revoke" msgid="5526857743819590458">"Revocați"</string> + <string name="revoke" msgid="5526857743819590458">"Revocă"</string> <string name="mediasize_iso_a0" msgid="7039061159929977973">"ISO A0"</string> <string name="mediasize_iso_a1" msgid="4063589931031977223">"ISO A1"</string> <string name="mediasize_iso_a2" msgid="2779860175680233980">"ISO A2"</string> @@ -1826,27 +1826,27 @@ <string name="reason_unknown" msgid="5599739807581133337">"necunoscut"</string> <string name="reason_service_unavailable" msgid="5288405248063804713">"Serviciul de printare nu este activat"</string> <string name="print_service_installed_title" msgid="6134880817336942482">"Serviciul <xliff:g id="NAME">%s</xliff:g> a fost instalat"</string> - <string name="print_service_installed_message" msgid="7005672469916968131">"Atingeți pentru a activa"</string> + <string name="print_service_installed_message" msgid="7005672469916968131">"Atinge pentru a activa"</string> <string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"Introdu codul PIN de administrator"</string> <string name="restr_pin_enter_pin" msgid="373139384161304555">"Introdu codul PIN"</string> <string name="restr_pin_incorrect" msgid="3861383632940852496">"Incorect"</string> <string name="restr_pin_enter_old_pin" msgid="7537079094090650967">"Codul PIN actual"</string> <string name="restr_pin_enter_new_pin" msgid="3267614461844565431">"Codul PIN nou"</string> - <string name="restr_pin_confirm_pin" msgid="7143161971614944989">"Confirmați noul cod PIN"</string> - <string name="restr_pin_create_pin" msgid="917067613896366033">"Creați un cod PIN pentru modificarea restricțiilor"</string> - <string name="restr_pin_error_doesnt_match" msgid="7063392698489280556">"Codurile PIN nu se potrivesc. Încercați din nou."</string> + <string name="restr_pin_confirm_pin" msgid="7143161971614944989">"Confirmă noul cod PIN"</string> + <string name="restr_pin_create_pin" msgid="917067613896366033">"Creează un cod PIN pentru modificarea restricțiilor"</string> + <string name="restr_pin_error_doesnt_match" msgid="7063392698489280556">"PIN-urile nu sunt identice. Încearcă din nou."</string> <string name="restr_pin_error_too_short" msgid="1547007808237941065">"Codul PIN este prea scurt. Trebuie să aibă cel puțin 4 cifre."</string> - <string name="restr_pin_try_later" msgid="5897719962541636727">"Reîncercați mai târziu"</string> + <string name="restr_pin_try_later" msgid="5897719962541636727">"Reîncearcă mai târziu"</string> <string name="immersive_cling_title" msgid="2307034298721541791">"Vizualizare pe ecran complet"</string> - <string name="immersive_cling_description" msgid="7092737175345204832">"Pentru a ieși, glisați de sus în jos."</string> + <string name="immersive_cling_description" msgid="7092737175345204832">"Pentru a ieși, glisează de sus în jos."</string> <string name="immersive_cling_positive" msgid="7047498036346489883">"Am înțeles"</string> <string name="done_label" msgid="7283767013231718521">"Terminat"</string> <string name="hour_picker_description" msgid="5153757582093524635">"Selector circular pentru ore"</string> <string name="minute_picker_description" msgid="9029797023621927294">"Selector circular pentru minute"</string> - <string name="select_hours" msgid="5982889657313147347">"Selectați orele"</string> - <string name="select_minutes" msgid="9157401137441014032">"Selectați minutele"</string> - <string name="select_day" msgid="2060371240117403147">"Selectați luna și ziua"</string> - <string name="select_year" msgid="1868350712095595393">"Selectați anul"</string> + <string name="select_hours" msgid="5982889657313147347">"Selectează orele"</string> + <string name="select_minutes" msgid="9157401137441014032">"Selectează minutele"</string> + <string name="select_day" msgid="2060371240117403147">"Selectează luna și ziua"</string> + <string name="select_year" msgid="1868350712095595393">"Selectează anul"</string> <string name="deleted_key" msgid="9130083334943364001">"<xliff:g id="KEY">%1$s</xliff:g> a fost șters"</string> <string name="managed_profile_label_badge" msgid="6762559569999499495">"<xliff:g id="LABEL">%1$s</xliff:g> de serviciu"</string> <string name="managed_profile_label_badge_2" msgid="5673187309555352550">"<xliff:g id="LABEL">%1$s</xliff:g> pentru serviciu (2)"</string> @@ -1854,15 +1854,15 @@ <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"Solicită codul PIN înainte de a anula fixarea"</string> <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Solicită mai întâi modelul pentru deblocare"</string> <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Solicită parola înainte de a anula fixarea"</string> - <string name="package_installed_device_owner" msgid="7035926868974878525">"Instalat de administratorul dvs."</string> - <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizat de administratorul dvs."</string> - <string name="package_deleted_device_owner" msgid="2292335928930293023">"Șters de administratorul dvs."</string> + <string name="package_installed_device_owner" msgid="7035926868974878525">"Instalat de administrator"</string> + <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizat de administrator"</string> + <string name="package_deleted_device_owner" msgid="2292335928930293023">"Șters de administrator"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Economisirea bateriei activează tema întunecată și restricționează sau dezactivează activitatea în fundal, unele efecte vizuale, alte funcții și câteva conexiuni la rețea."</string> <string name="battery_saver_description" msgid="8518809702138617167">"Economisirea bateriei activează tema întunecată și restricționează sau dezactivează activitatea în fundal, unele efecte vizuale, alte funcții și câteva conexiuni la rețea."</string> - <string name="data_saver_description" msgid="4995164271550590517">"Pentru a contribui la reducerea utilizării de date, Economizorul de date împiedică unele aplicații să trimită sau să primească date în fundal. O aplicație pe care o folosiți poate accesa datele, însă mai rar. Aceasta poate însemna, de exemplu, că imaginile se afișează numai după ce le atingeți."</string> - <string name="data_saver_enable_title" msgid="7080620065745260137">"Activați Economizorul de date?"</string> - <string name="data_saver_enable_button" msgid="4399405762586419726">"Activați"</string> + <string name="data_saver_description" msgid="4995164271550590517">"Pentru a contribui la reducerea utilizării de date, Economizorul de date împiedică unele aplicații să trimită sau să primească date în fundal. O aplicație pe care o folosești poate accesa datele, însă mai rar. Aceasta poate însemna, de exemplu, că imaginile se afișează numai după ce le atingi."</string> + <string name="data_saver_enable_title" msgid="7080620065745260137">"Activezi Economizorul de date?"</string> + <string name="data_saver_enable_button" msgid="4399405762586419726">"Activează"</string> <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Timp de un minut (până la {formattedTime})}few{Timp de # minute (până la {formattedTime})}other{Timp de # de minute (până la {formattedTime})}}"</string> <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Timp de un min. (până la {formattedTime})}few{Timp de # min. (până la {formattedTime})}other{Timp de # min. (până la {formattedTime})}}"</string> <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Timp de o oră (până la {formattedTime})}few{Timp de # ore (până la {formattedTime})}other{Timp de # de ore (până la {formattedTime})}}"</string> @@ -1874,10 +1874,10 @@ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Până <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string> <string name="zen_mode_until" msgid="2250286190237669079">"Până la <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string> <string name="zen_mode_alarm" msgid="7046911727540499275">"Până la <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (următoarea alarmă)"</string> - <string name="zen_mode_forever" msgid="740585666364912448">"Până când dezactivați"</string> - <string name="zen_mode_forever_dnd" msgid="3423201955704180067">"Până când dezactivați „Nu deranja”"</string> + <string name="zen_mode_forever" msgid="740585666364912448">"Până dezactivezi"</string> + <string name="zen_mode_forever_dnd" msgid="3423201955704180067">"Până când dezactivezi „Nu deranja”"</string> <string name="zen_mode_rule_name_combination" msgid="7174598364351313725">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string> - <string name="toolbar_collapse_description" msgid="8009920446193610996">"Restrângeți"</string> + <string name="toolbar_collapse_description" msgid="8009920446193610996">"Restrânge"</string> <string name="zen_mode_feature_name" msgid="3785547207263754500">"Nu deranja"</string> <string name="zen_mode_downtime_feature_name" msgid="5886005761431427128">"Inactivitate"</string> <string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"Nopțile din zilele lucrătoare"</string> @@ -1886,7 +1886,7 @@ <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Somn"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> dezactivează anumite sunete"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"A apărut o problemă internă pe dispozitiv, iar acesta poate fi instabil până la revenirea la setările din fabrică."</string> - <string name="system_error_manufacturer" msgid="703545241070116315">"A apărut o problemă internă pe dispozitiv. Pentru detalii, contactați producătorul."</string> + <string name="system_error_manufacturer" msgid="703545241070116315">"A apărut o problemă internă pe dispozitiv. Pentru detalii, contactează producătorul."</string> <string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"Solicitarea USSD a fost schimbată cu un apel obișnuit"</string> <string name="stk_cc_ussd_to_ss" msgid="4826846653052609738">"Solicitarea USSD a fost schimbată cu o solicitare SS"</string> <string name="stk_cc_ussd_to_ussd" msgid="8343001461299302472">"Schimbat cu o solicitare USSD nouă"</string> @@ -1899,77 +1899,77 @@ <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil de serviciu"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Notificat"</string> <string name="notification_verified_content_description" msgid="6401483602782359391">"Confirmat"</string> - <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Extindeți"</string> - <string name="expand_button_content_description_expanded" msgid="7484217944948667489">"Restrângeți"</string> - <string name="expand_action_accessibility" msgid="1947657036871746627">"extindeți/restrângeți"</string> + <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Extinde"</string> + <string name="expand_button_content_description_expanded" msgid="7484217944948667489">"Restrânge"</string> + <string name="expand_action_accessibility" msgid="1947657036871746627">"extinde/restrânge"</string> <string name="usb_midi_peripheral_name" msgid="490523464968655741">"Port USB Android periferic"</string> <string name="usb_midi_peripheral_manufacturer_name" msgid="7557148557088787741">"Android"</string> <string name="usb_midi_peripheral_product_name" msgid="2836276258480904434">"Port USB periferic"</string> <string name="floating_toolbar_open_overflow_description" msgid="2260297653578167367">"Mai multe opțiuni"</string> - <string name="floating_toolbar_close_overflow_description" msgid="3949818077708138098">"Închideți meniul suplimentar"</string> - <string name="maximize_button_text" msgid="4258922519914732645">"Maximizați"</string> - <string name="close_button_text" msgid="10603510034455258">"Închideți"</string> + <string name="floating_toolbar_close_overflow_description" msgid="3949818077708138098">"Închide meniul suplimentar"</string> + <string name="maximize_button_text" msgid="4258922519914732645">"Maximizează"</string> + <string name="close_button_text" msgid="10603510034455258">"Închide"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> - <string name="call_notification_answer_action" msgid="5999246836247132937">"Răspundeți"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Răspunde"</string> <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string> - <string name="call_notification_decline_action" msgid="3700345945214000726">"Respingeți"</string> - <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Încheiați"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Respinge"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Închide"</string> <string name="call_notification_incoming_text" msgid="6143109825406638201">"Apel primit"</string> <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Apel în desfășurare"</string> <string name="call_notification_screening_text" msgid="8396931408268940208">"Se filtrează un apel primit"</string> <string name="default_notification_channel_label" msgid="3697928973567217330">"Neclasificate"</string> - <string name="importance_from_user" msgid="2782756722448800447">"Dvs. setați importanța acestor notificări."</string> + <string name="importance_from_user" msgid="2782756722448800447">"Tu setezi importanța acestor notificări."</string> <string name="importance_from_person" msgid="4235804979664465383">"Notificarea este importantă având în vedere persoanele implicate."</string> <string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificare de aplicație personalizată"</string> - <string name="user_creation_account_exists" msgid="2239146360099708035">"Permiteți ca <xliff:g id="APP">%1$s</xliff:g> să creeze un nou utilizator folosind <xliff:g id="ACCOUNT">%2$s</xliff:g>? (există deja un utilizator cu acest cont)"</string> - <string name="user_creation_adding" msgid="7305185499667958364">"Permiteți ca <xliff:g id="APP">%1$s</xliff:g> să creeze un nou utilizator folosind <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string> - <string name="supervised_user_creation_label" msgid="6884904353827427515">"Adăugați un utilizator monitorizat"</string> - <string name="language_selection_title" msgid="52674936078683285">"Adăugați o limbă"</string> + <string name="user_creation_account_exists" msgid="2239146360099708035">"Permiți ca <xliff:g id="APP">%1$s</xliff:g> să creeze un nou utilizator folosind <xliff:g id="ACCOUNT">%2$s</xliff:g>? (există deja un utilizator cu acest cont)"</string> + <string name="user_creation_adding" msgid="7305185499667958364">"Permiți ca <xliff:g id="APP">%1$s</xliff:g> să creeze un nou utilizator folosind <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string> + <string name="supervised_user_creation_label" msgid="6884904353827427515">"Adaugă un utilizator monitorizat"</string> + <string name="language_selection_title" msgid="52674936078683285">"Adaugă o limbă"</string> <string name="country_selection_title" msgid="5221495687299014379">"Regiunea preferată"</string> <string name="search_language_hint" msgid="7004225294308793583">"Numele limbii"</string> <string name="language_picker_section_suggested" msgid="6556199184638990447">"Sugerate"</string> <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Sugerate"</string> <string name="language_picker_section_all" msgid="1985809075777564284">"Toate limbile"</string> <string name="region_picker_section_all" msgid="756441309928774155">"Toate regiunile"</string> - <string name="locale_search_menu" msgid="6258090710176422934">"Căutați"</string> + <string name="locale_search_menu" msgid="6258090710176422934">"Caută"</string> <string name="app_suspended_title" msgid="888873445010322650">"Aplicația nu este disponibilă"</string> <string name="app_suspended_default_message" msgid="6451215678552004172">"Momentan, aplicația <xliff:g id="APP_NAME_0">%1$s</xliff:g> nu este disponibilă. Aceasta este gestionată de <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> - <string name="app_suspended_more_details" msgid="211260942831587014">"Aflați mai multe"</string> + <string name="app_suspended_more_details" msgid="211260942831587014">"Află mai multe"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Anulează întreruperea aplicației"</string> - <string name="work_mode_off_title" msgid="961171256005852058">"Activați aplicațiile pentru lucru?"</string> - <string name="work_mode_off_message" msgid="7319580997683623309">"Obțineți acces la aplicațiile pentru lucru și notificări"</string> - <string name="work_mode_turn_on" msgid="3662561662475962285">"Activați"</string> + <string name="work_mode_off_title" msgid="961171256005852058">"Activezi aplicațiile pentru lucru?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Obține acces la aplicațiile și notificările pentru lucru"</string> + <string name="work_mode_turn_on" msgid="3662561662475962285">"Activează"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Aplicația nu este disponibilă"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu este disponibilă momentan."</string> <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nu este disponibilă"</string> <string name="app_streaming_blocked_title_for_permission_dialog" msgid="4483161748582966785">"Necesită permisiune"</string> <string name="app_streaming_blocked_title_for_camera_dialog" msgid="3935701653713853065">"Camera video nu este disponibilă"</string> - <string name="app_streaming_blocked_title_for_fingerprint_dialog" msgid="3516853717714141951">"Continuați pe telefon"</string> + <string name="app_streaming_blocked_title_for_fingerprint_dialog" msgid="3516853717714141951">"Continuă pe telefon"</string> <string name="app_streaming_blocked_title_for_microphone_dialog" msgid="544822455127171206">"Microfon indisponibil"</string> <string name="app_streaming_blocked_title_for_playstore_dialog" msgid="8149823099822897538">"Aplicația Magazin Play nu este disponibilă"</string> <string name="app_streaming_blocked_title_for_settings_dialog" product="tv" msgid="196994247017450357">"Setările pentru Android TV sunt indisponibile"</string> <string name="app_streaming_blocked_title_for_settings_dialog" product="tablet" msgid="8222710146267948647">"Setările pentru tabletă sunt indisponibile"</string> <string name="app_streaming_blocked_title_for_settings_dialog" product="default" msgid="6895719984375299791">"Setările pentru telefon sunt indisponibile"</string> - <string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"Aplicația nu poate fi accesată pe <xliff:g id="DEVICE">%1$s</xliff:g> momentan. Încercați pe dispozitivul Android TV."</string> - <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"Aplicația nu poate fi accesată pe <xliff:g id="DEVICE">%1$s</xliff:g> momentan. Încercați pe tabletă."</string> - <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"Aplicația nu poate fi accesată pe <xliff:g id="DEVICE">%1$s</xliff:g> momentan. Încercați pe telefon."</string> - <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Aplicația necesită securitate suplimentară. Încercați pe dispozitivul Android TV."</string> - <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Aplicația necesită securitate suplimentară. Încercați pe tabletă."</string> - <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Aplicația necesită securitate suplimentară. Încercați pe telefon."</string> + <string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"Aplicația nu poate fi accesată pe <xliff:g id="DEVICE">%1$s</xliff:g> momentan. Încearcă pe dispozitivul Android TV."</string> + <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"Aplicația nu poate fi accesată pe <xliff:g id="DEVICE">%1$s</xliff:g> momentan. Încearcă pe tabletă."</string> + <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"Aplicația nu poate fi accesată pe <xliff:g id="DEVICE">%1$s</xliff:g> momentan. Încearcă pe telefon."</string> + <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Aplicația necesită securitate suplimentară. Încearcă pe dispozitivul Android TV."</string> + <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Aplicația necesită securitate suplimentară. Încearcă pe tabletă."</string> + <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Aplicația necesită securitate suplimentară. Încearcă pe telefon."</string> <string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Nu se poate accesa pe <xliff:g id="DEVICE">%1$s</xliff:g>. Încearcă pe dispozitivul Android TV."</string> <string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Nu se poate accesa pe <xliff:g id="DEVICE">%1$s</xliff:g>. Încearcă pe tabletă."</string> <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Nu se poate accesa pe <xliff:g id="DEVICE">%1$s</xliff:g>. Încearcă pe telefon."</string> - <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Această aplicație a fost creată pentru o versiune Android mai veche și este posibil să nu funcționeze corect. Încercați să căutați actualizări sau contactați dezvoltatorul."</string> - <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Căutați actualizări"</string> - <string name="new_sms_notification_title" msgid="6528758221319927107">"Aveți mesaje noi"</string> - <string name="new_sms_notification_content" msgid="3197949934153460639">"Deschideți aplicația pentru SMS-uri ca să vizualizați"</string> + <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Această aplicație a fost creată pentru o versiune Android mai veche și e posibil să nu funcționeze corect. Încearcă să cauți actualizări sau contactează dezvoltatorul."</string> + <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Caută actualizări"</string> + <string name="new_sms_notification_title" msgid="6528758221319927107">"Ai mesaje noi"</string> + <string name="new_sms_notification_content" msgid="3197949934153460639">"Deschide aplicația pentru SMS-uri ca să vezi"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Unele funcții ar putea fi limitate"</string> <string name="profile_encrypted_detail" msgid="5279730442756849055">"Profil de serviciu blocat"</string> - <string name="profile_encrypted_message" msgid="1128512616293157802">"Atingeți ca să deblocați"</string> + <string name="profile_encrypted_message" msgid="1128512616293157802">"Atinge ca să deblochezi"</string> <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"Conectat la <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string> - <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Atingeți pentru a vedea fișierele"</string> - <string name="pin_target" msgid="8036028973110156895">"Fixați"</string> - <string name="pin_specific_target" msgid="7824671240625957415">"Fixați <xliff:g id="LABEL">%1$s</xliff:g>"</string> + <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Atinge pentru a vedea fișierele"</string> + <string name="pin_target" msgid="8036028973110156895">"Fixează"</string> + <string name="pin_specific_target" msgid="7824671240625957415">"Fixează <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="unpin_target" msgid="3963318576590204447">"Anulează fixarea"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"Anulează fixarea pentru <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="app_info" msgid="6113278084877079851">"Informații despre aplicație"</string> @@ -1992,30 +1992,30 @@ <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Remedierea erorilor prin USB"</string> <string name="time_picker_hour_label" msgid="4208590187662336864">"oră"</string> <string name="time_picker_minute_label" msgid="8307452311269824553">"minut"</string> - <string name="time_picker_header_text" msgid="9073802285051516688">"Setați ora"</string> + <string name="time_picker_header_text" msgid="9073802285051516688">"Setează ora"</string> <string name="time_picker_input_error" msgid="8386271930742451034">"Introdu o oră validă"</string> <string name="time_picker_prompt_label" msgid="303588544656363889">"Introdu ora"</string> - <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Pentru a introduce ora, comutați la modul de introducere a textului."</string> - <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Pentru a introduce ora, comutați la modul ceas."</string> + <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Pentru a introduce ora, comută la modul de introducere a textului."</string> + <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Pentru a introduce ora, comută la modul ceas."</string> <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Opțiuni de completare automată"</string> - <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Salvați pentru completare automată"</string> + <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Salvează pentru completare automată"</string> <string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Conținutul nu poate fi completat automat"</string> <string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Nicio sugestie de completare automată"</string> <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{O sugestie de completare automată}few{# sugestii de completare automată}other{# de sugestii de completare automată}}"</string> - <string name="autofill_save_title" msgid="7719802414283739775">"Salvați în "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string> - <string name="autofill_save_title_with_type" msgid="3002460014579799605">"Salvați <xliff:g id="TYPE">%1$s</xliff:g> în "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string> - <string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Salvați <xliff:g id="TYPE_0">%1$s</xliff:g> și <xliff:g id="TYPE_1">%2$s</xliff:g> în "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string> - <string name="autofill_save_title_with_3types" msgid="6598228952100102578">"Salvați <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> și <xliff:g id="TYPE_2">%3$s</xliff:g> în "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string> - <string name="autofill_update_title" msgid="3630695947047069136">"Actualizați în "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string> - <string name="autofill_update_title_with_type" msgid="5264152633488495704">"Actualizați <xliff:g id="TYPE">%1$s</xliff:g> în "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string> - <string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Actualizați <xliff:g id="TYPE_0">%1$s</xliff:g> și <xliff:g id="TYPE_1">%2$s</xliff:g> în "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string> - <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Actualizați aceste articole în "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> și <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string> - <string name="autofill_save_yes" msgid="8035743017382012850">"Salvați"</string> + <string name="autofill_save_title" msgid="7719802414283739775">"Salvezi în "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string> + <string name="autofill_save_title_with_type" msgid="3002460014579799605">"Salvezi <xliff:g id="TYPE">%1$s</xliff:g> în "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string> + <string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Salvezi <xliff:g id="TYPE_0">%1$s</xliff:g> și <xliff:g id="TYPE_1">%2$s</xliff:g> în "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string> + <string name="autofill_save_title_with_3types" msgid="6598228952100102578">"Salvezi <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> și <xliff:g id="TYPE_2">%3$s</xliff:g> în "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string> + <string name="autofill_update_title" msgid="3630695947047069136">"Actualizezi în "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string> + <string name="autofill_update_title_with_type" msgid="5264152633488495704">"Actualizezi <xliff:g id="TYPE">%1$s</xliff:g> în "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string> + <string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Actualizezi <xliff:g id="TYPE_0">%1$s</xliff:g> și <xliff:g id="TYPE_1">%2$s</xliff:g> în "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string> + <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Actualizezi aceste articole în "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> și <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string> + <string name="autofill_save_yes" msgid="8035743017382012850">"Salvează"</string> <string name="autofill_save_no" msgid="9212826374207023544">"Nu, mulțumesc"</string> <string name="autofill_save_notnow" msgid="2853932672029024195">"Nu acum"</string> <string name="autofill_save_never" msgid="6821841919831402526">"Niciodată"</string> - <string name="autofill_update_yes" msgid="4608662968996874445">"Actualizați"</string> - <string name="autofill_continue_yes" msgid="7914985605534510385">"Continuați"</string> + <string name="autofill_update_yes" msgid="4608662968996874445">"Actualizează"</string> + <string name="autofill_continue_yes" msgid="7914985605534510385">"Continuă"</string> <string name="autofill_save_type_password" msgid="5624528786144539944">"parolă"</string> <string name="autofill_save_type_address" msgid="3111006395818252885">"adresă"</string> <string name="autofill_save_type_credit_card" msgid="3583795235862046693">"card de credit"</string> @@ -2024,11 +2024,11 @@ <string name="autofill_save_type_generic_card" msgid="1019367283921448608">"card"</string> <string name="autofill_save_type_username" msgid="1018816929884640882">"nume de utilizator"</string> <string name="autofill_save_type_email_address" msgid="1303262336895591924">"adresă de e-mail"</string> - <string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"Păstrați-vă calmul și căutați un adăpost în apropiere."</string> - <string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"Părăsiți imediat zonele de coastă și din apropierea râurilor și îndreptați-vă spre un loc mai sigur, cum ar fi o zonă aflată la înălțime."</string> - <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="4888224011071875068">"Păstrați-vă calmul și căutați un adăpost în apropiere."</string> + <string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"Păstrează-ți calmul și caută un adăpost în apropiere."</string> + <string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"Părăsește imediat zonele de coastă și din apropierea râurilor și îndreaptă-te spre un loc mai sigur, cum ar fi o zonă aflată la înălțime."</string> + <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="4888224011071875068">"Păstrează-ți calmul și caută un adăpost în apropiere."</string> <string name="etws_primary_default_message_test" msgid="4583367373909549421">"Testarea mesajelor de urgență"</string> - <string name="notification_reply_button_accessibility" msgid="5235776156579456126">"Răspundeți"</string> + <string name="notification_reply_button_accessibility" msgid="5235776156579456126">"Răspunde"</string> <string name="etws_primary_default_message_others" msgid="7958161706019130739"></string> <string name="mmcc_authentication_reject" msgid="4891965994643876369">"Cardul SIM nu este permis pentru voce"</string> <string name="mmcc_imsi_unknown_in_hlr" msgid="227760698553988751">"Cardul SIM nu este activat pentru voce"</string> @@ -2045,47 +2045,49 @@ <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"Nu s-a putut restabili comanda rapidă din cauza nepotrivirii semnăturii aplicației"</string> <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"Nu s-a putut restabili comanda rapidă"</string> <string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"Comanda rapidă este dezactivată"</string> - <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"DEZINSTALAȚI"</string> - <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"Deschideți oricum"</string> + <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"DEZINSTALEAZĂ"</string> + <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"Deschide oricum"</string> <string name="harmful_app_warning_title" msgid="8794823880881113856">"Aplicație dăunătoare detectată"</string> - <string name="log_access_confirmation_title" msgid="2343578467290592708">"Permiteți ca <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> să acceseze toate jurnalele dispozitivului?"</string> - <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permiteți accesul o dată"</string> - <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nu permiteți"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Jurnalele dispozitivului înregistrează activitatea de pe dispozitivul tău. Aplicațiile pot folosi aceste jurnale pentru a identifica și a remedia probleme.\n\nUnele jurnale pot să conțină informații sensibile, prin urmare permite accesul la toate jurnalele dispozitivului doar aplicațiilor în care ai încredere. \n\nDacă nu permiți accesul aplicației la toate jurnalele dispozitivului, aceasta poate în continuare să acceseze propriile jurnale. Este posibil ca producătorul dispozitivului să acceseze în continuare unele jurnale sau informații de pe dispozitiv."</string> + <string name="log_access_confirmation_title" msgid="2343578467290592708">"Permiți ca <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> să acceseze toate jurnalele dispozitivului?"</string> + <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permite accesul o dată"</string> + <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nu permite"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Jurnalele dispozitivului înregistrează activitatea de pe dispozitivul tău. Aplicațiile pot folosi aceste jurnale pentru a identifica și a remedia probleme.\n\nUnele jurnale pot să conțină informații sensibile, prin urmare permite accesul la toate jurnalele dispozitivului doar aplicațiilor în care ai încredere. \n\nDacă nu permiți accesul aplicației la toate jurnalele dispozitivului, aceasta poate în continuare să acceseze propriile jurnale. Este posibil ca producătorul dispozitivului să acceseze în continuare unele jurnale sau informații de pe dispozitiv."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Nu mai afișa"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> vrea să afișeze porțiuni din <xliff:g id="APP_2">%2$s</xliff:g>"</string> - <string name="screenshot_edit" msgid="7408934887203689207">"Editați"</string> + <string name="screenshot_edit" msgid="7408934887203689207">"Editează"</string> <string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"Apelurile și notificările vor vibra"</string> <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"Apelurile și notificările vor avea sunetul dezactivat"</string> <string name="notification_channel_system_changes" msgid="2462010596920209678">"Modificări de sistem"</string> <string name="notification_channel_do_not_disturb" msgid="7832584281883687653">"Nu deranja"</string> <string name="zen_upgrade_notification_visd_title" msgid="2001148984371968620">"Funcția nouă Nu deranja ascunde notificările"</string> - <string name="zen_upgrade_notification_visd_content" msgid="3683314609114134946">"Atingeți ca să aflați mai multe și să modificați"</string> + <string name="zen_upgrade_notification_visd_content" msgid="3683314609114134946">"Atinge ca să afli mai multe și să modifici"</string> <string name="zen_upgrade_notification_title" msgid="8198167698095298717">"Funcția Nu deranja s-a schimbat"</string> - <string name="zen_upgrade_notification_content" msgid="5228458567180124005">"Atingeți pentru a verifica ce este blocat."</string> - <string name="review_notification_settings_title" msgid="5102557424459810820">"Examinați setările pentru notificări"</string> - <string name="review_notification_settings_text" msgid="5916244866751849279">"Începând cu Android 13, aplicațiile pe care le instalați necesită permisiunea de a trimite notificări. Atingeți ca să modificați permisiunea pentru aplicațiile existente."</string> + <string name="zen_upgrade_notification_content" msgid="5228458567180124005">"Atinge pentru a verifica ce este blocat."</string> + <string name="review_notification_settings_title" msgid="5102557424459810820">"Verifică setările pentru notificări"</string> + <string name="review_notification_settings_text" msgid="5916244866751849279">"Începând cu Android 13, aplicațiile pe care le instalezi necesită permisiunea de a trimite notificări. Atinge ca să modifici permisiunea pentru aplicațiile existente."</string> <string name="review_notification_settings_remind_me_action" msgid="1081081018678480907">"Mai târziu"</string> - <string name="review_notification_settings_dismiss" msgid="4160916504616428294">"Închideți"</string> + <string name="review_notification_settings_dismiss" msgid="4160916504616428294">"Închide"</string> <string name="notification_app_name_system" msgid="3045196791746735601">"Sistem"</string> <string name="notification_app_name_settings" msgid="9088548800899952531">"Setări"</string> <string name="notification_appops_camera_active" msgid="8177643089272352083">"Cameră foto"</string> <string name="notification_appops_microphone_active" msgid="581333393214739332">"Microfon"</string> <string name="notification_appops_overlay_active" msgid="5571732753262836481">"se afișează peste alte aplicații de pe ecran"</string> - <string name="notification_feedback_indicator" msgid="663476517711323016">"Oferiți feedback"</string> - <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"Notificarea a fost promovată la Prestabilită. Atingeți pentru a oferi feedback."</string> - <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Notificarea a fost mutată în jos la Silențioasă. Atingeți pentru a oferi feedback."</string> - <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Notificarea a fost mutată la un nivel superior. Atingeți pentru a oferi feedback."</string> - <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Notificarea a fost mutată la un nivel inferior. Atingeți pentru a oferi feedback."</string> + <string name="notification_feedback_indicator" msgid="663476517711323016">"Oferă feedback"</string> + <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"Notificarea a fost promovată la Prestabilită. Atinge pentru a oferi feedback."</string> + <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Notificarea a fost mutată în jos la Silențioasă. Atinge pentru a oferi feedback."</string> + <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Notificarea a fost mutată la un nivel superior. Atinge pentru a oferi feedback."</string> + <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Notificarea a fost mutată la un nivel inferior. Atinge pentru a oferi feedback."</string> <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificări optimizate"</string> <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"Acțiunile și răspunsurile sugerate sunt acum trimise prin notificări optimizate. Notificările adaptive Android nu mai sunt acceptate."</string> <string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string> - <string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Dezactivați"</string> - <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Aflați mai multe"</string> - <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Notificările optimizate au înlocuit Notificările adaptive Android de pe Android 12. Această funcție afișează acțiuni și răspunsuri sugerate și vă organizează notificările.\n\nNotificările optimizate pot accesa conținutul notificărilor, inclusiv informații cu caracter personal, precum mesajele și numele persoanelor de contact. În plus, funcția poate să închidă sau să răspundă la notificări, de exemplu, să răspundă la apeluri telefonice și să gestioneze opțiunea Nu deranja."</string> + <string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Dezactivează"</string> + <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Află mai multe"</string> + <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Notificările optimizate au înlocuit Notificările adaptive Android de pe Android 12. Această funcție afișează acțiuni și răspunsuri sugerate și organizează notificările.\n\nNotificările optimizate pot accesa conținutul notificărilor, inclusiv informații cu caracter personal, precum mesajele și numele persoanelor de contact. În plus, funcția poate să închidă sau să răspundă la notificări, de exemplu, să răspundă la apeluri telefonice și să gestioneze opțiunea Nu deranja."</string> <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificare pentru informații despre modul Rutină"</string> <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Bateria se poate descărca înainte de încărcarea obișnuită"</string> - <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Economisirea bateriei este activată pentru a prelungi durata de funcționare a bateriei"</string> + <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Economisirea bateriei este activată pentru a mări autonomia"</string> <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Economisirea bateriei"</string> <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Economisirea bateriei a fost dezactivată"</string> <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Telefonul este încărcat suficient. Funcțiile nu mai sunt limitate."</string> @@ -2127,7 +2129,7 @@ <string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"Comandă rapidă de accesibilitate de pe ecran"</string> <string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Selector de comenzi rapide de accesibilitate de pe ecran"</string> <string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Comandă rapidă de accesibilitate"</string> - <string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Închideți fereastra de notificări"</string> + <string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Închide fereastra de notificări"</string> <string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad sus"</string> <string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad jos"</string> <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad stânga"</string> @@ -2150,13 +2152,13 @@ <string name="resolver_cant_share_with_personal_apps_explanation" msgid="6349766201904601544">"Acest conținut nu poate fi trimis cu aplicații personale"</string> <string name="resolver_cant_access_personal_apps_explanation" msgid="1679399548862724359">"Acest conținut nu poate fi deschis cu aplicații personale"</string> <string name="resolver_turn_on_work_apps" msgid="884910835250037247">"Profilul de serviciu este întrerupt"</string> - <string name="resolver_switch_on_work" msgid="463709043650610420">"Atingeți pentru a activa"</string> + <string name="resolver_switch_on_work" msgid="463709043650610420">"Atinge pentru a activa"</string> <string name="resolver_no_work_apps_available" msgid="3298291360133337270">"Nicio aplicație pentru lucru"</string> <string name="resolver_no_personal_apps_available" msgid="6284837227019594881">"Nicio aplicație personală"</string> - <string name="miniresolver_open_in_personal" msgid="3874522693661065566">"Deschideți <xliff:g id="APP">%s</xliff:g> în profilul personal?"</string> - <string name="miniresolver_open_in_work" msgid="4415223793669536559">"Deschideți <xliff:g id="APP">%s</xliff:g> în profilul de serviciu?"</string> - <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Folosiți browserul personal"</string> - <string name="miniresolver_use_work_browser" msgid="543575306251952994">"Folosiți browserul de serviciu"</string> + <string name="miniresolver_open_in_personal" msgid="3874522693661065566">"Deschizi <xliff:g id="APP">%s</xliff:g> în profilul personal?"</string> + <string name="miniresolver_open_in_work" msgid="4415223793669536559">"Deschizi <xliff:g id="APP">%s</xliff:g> în profilul de serviciu?"</string> + <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Folosește browserul personal"</string> + <string name="miniresolver_use_work_browser" msgid="543575306251952994">"Folosește browserul de serviciu"</string> <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY" msgid="8050953231914637819">"Codul PIN de deblocare SIM privind rețeaua"</string> <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ENTRY" msgid="7164399703751688214">"Codul PIN de deblocare SIM privind subsetul de rețea"</string> <string name="PERSOSUBSTATE_SIM_CORPORATE_ENTRY" msgid="4447629474818217364">"Codul PIN de deblocare SIM corporativă"</string> @@ -2270,26 +2272,26 @@ <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> <string name="window_magnification_prompt_title" msgid="2876703640772778215">"Noi setări de mărire"</string> - <string name="window_magnification_prompt_content" msgid="8159173903032344891">"Acum puteți mări o parte a ecranului"</string> - <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Activați din Setări"</string> - <string name="dismiss_action" msgid="1728820550388704784">"Respingeți"</string> - <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Deblocați microfonul dispozitivului"</string> - <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Deblocați camera dispozitivului"</string> + <string name="window_magnification_prompt_content" msgid="8159173903032344891">"Acum poți mări o parte a ecranului"</string> + <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Activează din Setări"</string> + <string name="dismiss_action" msgid="1728820550388704784">"Închide"</string> + <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Deblochează microfonul dispozitivului"</string> + <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Deblochează camera dispozitivului"</string> <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Pentru <b><xliff:g id="APP">%s</xliff:g></b> și toate aplicațiile și serviciile"</string> - <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Deblocați"</string> + <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Deblochează"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Confidențialitatea privind senzorii"</string> <string name="splash_screen_view_icon_description" msgid="180638751260598187">"Pictograma aplicației"</string> <string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Imaginea de branding a aplicației"</string> - <string name="view_and_control_notification_title" msgid="4300765399209912240">"Verificați setările pentru acces"</string> - <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> poate să vadă și să vă controleze ecranul. Atingeți pentru a examina."</string> + <string name="view_and_control_notification_title" msgid="4300765399209912240">"Verifică setările pentru acces"</string> + <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> poate să vadă și să controleze ecranul. Atinge pentru a verifica."</string> <string name="ui_translation_accessibility_translated_text" msgid="3197547218178944544">"<xliff:g id="MESSAGE">%1$s</xliff:g> a fost tradus."</string> <string name="ui_translation_accessibility_translation_finished" msgid="3057830947610088465">"Mesaj tradus din <xliff:g id="FROM_LANGUAGE">%1$s</xliff:g> în <xliff:g id="TO_LANGUAGE">%2$s</xliff:g>."</string> <string name="notification_channel_abusive_bg_apps" msgid="6092140213264920355">"Activitate de fundal"</string> <string name="notification_title_abusive_bg_apps" msgid="994230770856147656">"O aplicație consumă bateria"</string> <string name="notification_title_long_running_fgs" msgid="8170284286477131587">"O aplicație este încă activă"</string> - <string name="notification_content_abusive_bg_apps" msgid="5296898075922695259">"<xliff:g id="APP">%1$s</xliff:g> rulează în fundal. Atingeți pentru a gestiona utilizarea bateriei."</string> - <string name="notification_content_long_running_fgs" msgid="8258193410039977101">"<xliff:g id="APP">%1$s</xliff:g> poate afecta autonomia bateriei. Atingeți pentru a examina aplicațiile active."</string> - <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Verificați aplicațiile active"</string> + <string name="notification_content_abusive_bg_apps" msgid="5296898075922695259">"<xliff:g id="APP">%1$s</xliff:g> rulează în fundal. Atinge pentru a gestiona utilizarea bateriei."</string> + <string name="notification_content_long_running_fgs" msgid="8258193410039977101">"<xliff:g id="APP">%1$s</xliff:g> poate afecta autonomia bateriei. Atinge pentru a examina aplicațiile active."</string> + <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Verifică aplicațiile active"</string> <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nu se poate accesa camera foto a telefonului de pe <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nu se poate accesa camera foto a tabletei de pe <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Nu se poate accesa în timpul streamingului. Încearcă pe telefon."</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index c4dce39f91bd..d01e430efd84 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -268,7 +268,7 @@ <string name="global_action_settings" msgid="4671878836947494217">"Настройки"</string> <string name="global_action_assist" msgid="2517047220311505805">"Помощник"</string> <string name="global_action_voice_assist" msgid="6655788068555086695">"Аудиоподсказки"</string> - <string name="global_action_lockdown" msgid="2475471405907902963">"Блокировка"</string> + <string name="global_action_lockdown" msgid="2475471405907902963">"Блокировка входа"</string> <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">">999"</string> <string name="notification_hidden_text" msgid="2835519769868187223">"Новое уведомление"</string> <string name="notification_channel_virtual_keyboard" msgid="6465975799223304567">"Виртуальная клавиатура"</string> @@ -2052,7 +2052,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Разрешить приложению \"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>\" доступ ко всем журналам устройства?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Разрешить разовый доступ"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Запретить"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"В журналы записывается информация о том, что происходит на устройстве. Приложения могут использовать их, чтобы находить и устранять неполадки.\n\nТак как некоторые журналы могут содержать конфиденциальную информацию, доступ ко всем журналам следует предоставлять только тем приложениям, которым вы доверяете. \n\nЕсли вы не предоставите такой доступ этому приложению, оно по-прежнему сможет просматривать свои журналы. Не исключено, что некоторые журналы или сведения на вашем устройстве будут по-прежнему доступны его производителю."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"В журналы записывается информация о том, что происходит на устройстве. Приложения могут использовать их, чтобы находить и устранять неполадки.\n\nТак как некоторые журналы могут содержать конфиденциальную информацию, доступ ко всем журналам следует предоставлять только тем приложениям, которым вы доверяете. \n\nЕсли вы не предоставите такой доступ этому приложению, оно по-прежнему сможет просматривать свои журналы. Не исключено, что некоторые журналы или сведения на вашем устройстве будут по-прежнему доступны его производителю."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Больше не показывать"</string> <string name="slices_permission_request" msgid="3677129866636153406">"Приложение \"<xliff:g id="APP_0">%1$s</xliff:g>\" запрашивает разрешение на показ фрагментов приложения \"<xliff:g id="APP_2">%2$s</xliff:g>\"."</string> <string name="screenshot_edit" msgid="7408934887203689207">"Изменить"</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 3d1a4419254d..ac0a3f6c7431 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> හට සියලු උපාංග ලොග ප්රවේශ වීමට ඉඩ දෙන්නද?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"එක් වරක් ප්රවේශය ඉඩ දෙන්න"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"ඉඩ නොදෙන්න"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"උපාංග ලොග ඔබේ උපාංගයෙහි සිදු වන දේ වාර්තා කරයි. ගැටලු සොයා ගැනීමට සහ නිරාකරණයට යෙදුම්වලට මෙම ලොග භාවිතා කළ හැක.\n\nසමහර ලොගවල සංවේදී තතු අඩංගු විය හැකි බැවින්, ඔබ විශ්වාස කරන යෙදුම්වලට පමණක් සියලු උපාංග ලොග වෙත ප්රවේශ වීමට ඉඩ දෙන්න. \n\nඔබ මෙම යෙදුමට සියලු උපාංග ලොග වෙත ප්රවේශ වීමට ඉඩ නොදෙන්නේ නම්, එයට තවමත් එහිම ලොග වෙත ප්රවේශ විය හැක. ඔබේ උපාංග නිෂ්පාදකයාට තවමත් ඔබේ උපාංගයෙහි සමහර ලොග හෝ තතු වෙත ප්රවේශ විය හැක."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"උපාංග ලොග ඔබේ උපාංගයෙහි සිදු වන දේ වාර්තා කරයි. ගැටලු සොයා ගැනීමට සහ නිරාකරණයට යෙදුම්වලට මෙම ලොග භාවිතා කළ හැක.\n\nසමහර ලොගවල සංවේදී තතු අඩංගු විය හැකි බැවින්, ඔබ විශ්වාස කරන යෙදුම්වලට පමණක් සියලු උපාංග ලොග වෙත ප්රවේශ වීමට ඉඩ දෙන්න. \n\nඔබ මෙම යෙදුමට සියලු උපාංග ලොග වෙත ප්රවේශ වීමට ඉඩ නොදෙන්නේ නම්, එයට තවමත් එහිම ලොග වෙත ප්රවේශ විය හැක. ඔබේ උපාංග නිෂ්පාදකයාට තවමත් ඔබේ උපාංගයෙහි සමහර ලොග හෝ තතු වෙත ප්රවේශ විය හැක."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"නැවත නොපෙන්වන්න"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> හට කොටස් <xliff:g id="APP_2">%2$s</xliff:g>ක් පෙන්වීමට අවශ්යයි"</string> <string name="screenshot_edit" msgid="7408934887203689207">"සංස්කරණය"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index c2f906ef4c5f..e28df1008809 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -2052,7 +2052,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Chcete povoliť aplikácii <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> prístup k všetkým denníkom zariadenia?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Povoliť jednorazový prístup"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nepovoliť"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Denníky zariadenia zaznamenávajú, čo sa deje vo vašom zariadení. Aplikácie môžu pomocou týchto denníkov vyhľadávať a riešiť problémy.\n\nNiektoré denníky môžu obsahovať citlivé údaje, preto povoľte prístup k všetkým denníkom zariadenia iba dôveryhodným aplikáciám. \n\nAk tejto aplikácii nepovolíte prístup k všetkým denníkom zariadenia, stále bude mať prístup k vlastným denníkom. Výrobca vášho zariadenia bude mať naďalej prístup k niektorým denníkom alebo informáciám vo vašom zariadení."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Denníky zariadenia zaznamenávajú, čo sa deje vo vašom zariadení. Aplikácie môžu pomocou týchto denníkov vyhľadávať a riešiť problémy.\n\nNiektoré denníky môžu obsahovať citlivé údaje, preto povoľte prístup k všetkým denníkom zariadenia iba dôveryhodným aplikáciám. \n\nAk tejto aplikácii nepovolíte prístup k všetkým denníkom zariadenia, stále bude mať prístup k vlastným denníkom. Výrobca vášho zariadenia bude mať naďalej prístup k niektorým denníkom alebo informáciám vo vašom zariadení."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Už nezobrazovať"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> chce zobrazovať rezy z aplikácie <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Upraviť"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 9e6c571af36d..56e1b5c4b580 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -2052,7 +2052,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Ali aplikaciji <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> dovolite dostop do vseh dnevnikov naprave?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Dovoli enkratni dostop"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ne dovoli"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"V dnevnikih naprave se beleži dogajanje v napravi. Aplikacije lahko te dnevnike uporabijo za iskanje in odpravljanje težav.\n\nNekateri dnevniki morda vsebujejo občutljive podatke, zato dostop do vseh dnevnikov naprave omogočite le aplikacijam, ki jim zaupate. \n\nČe tej aplikaciji ne dovolite dostopa do vseh dnevnikov naprave, bo aplikacija kljub temu lahko dostopala do svojih dnevnikov. Proizvajalec naprave bo morda lahko kljub temu dostopal do nekaterih dnevnikov ali podatkov v napravi."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"V dnevnikih naprave se beleži dogajanje v napravi. Aplikacije lahko te dnevnike uporabijo za iskanje in odpravljanje težav.\n\nNekateri dnevniki morda vsebujejo občutljive podatke, zato dostop do vseh dnevnikov naprave omogočite le aplikacijam, ki jim zaupate. \n\nČe tej aplikaciji ne dovolite dostopa do vseh dnevnikov naprave, bo aplikacija kljub temu lahko dostopala do svojih dnevnikov. Proizvajalec naprave bo morda lahko kljub temu dostopal do nekaterih dnevnikov ali podatkov v napravi."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ne prikaži več"</string> <string name="slices_permission_request" msgid="3677129866636153406">"Aplikacija <xliff:g id="APP_0">%1$s</xliff:g> želi prikazati izreze aplikacije <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Uredi"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index dee369165c6a..bb55457aec64 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Të lejohet që <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> të ketë qasje te të gjitha evidencat e pajisjes?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Lejo qasjen vetëm për një herë"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Mos lejo"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Evidencat e pajisjes regjistrojnë çfarë ndodh në pajisjen tënde. Aplikacionet mund t\'i përdorin këto evidenca për të gjetur dhe rregulluar problemet.\n\nDisa evidenca mund të përmbajnë informacione delikate, ndaj lejo vetëm aplikacionet që u beson të kenë qasje te të gjitha evidencat e pajisjes. \n\nNëse nuk e lejon këtë aplikacion që të ketë qasje te të gjitha evidencat e pajisjes, ai mund të vazhdojë të ketë qasje tek evidencat e tij. Prodhuesi i pajisjes sate mund të jetë ende në gjendje që të ketë qasje te disa evidenca ose informacione në pajisjen tënde."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Evidencat e pajisjes regjistrojnë çfarë ndodh në pajisjen tënde. Aplikacionet mund t\'i përdorin këto evidenca për të gjetur dhe rregulluar problemet.\n\nDisa evidenca mund të përmbajnë informacione delikate, ndaj lejo vetëm aplikacionet që u beson të kenë qasje te të gjitha evidencat e pajisjes. \n\nNëse nuk e lejon këtë aplikacion që të ketë qasje te të gjitha evidencat e pajisjes, ai mund të vazhdojë të ketë qasje tek evidencat e tij. Prodhuesi i pajisjes sate mund të jetë ende në gjendje që të ketë qasje te disa evidenca ose informacione në pajisjen tënde."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Mos e shfaq më"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> dëshiron të shfaqë pjesë të <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Modifiko"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 0a5c4c0456cc..57a9a5b868af 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -2051,7 +2051,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Желите да дозволите апликацији <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> да приступа свим евиденцијама уређаја?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Дозволи једнократан приступ"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Не дозволи"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Евиденције уређаја региструју шта се дешава на уређају. Апликације могу да користе те евиденције да би пронашле и решиле проблеме.\n\nНеке евиденције могу да садрже осетљиве информације, па приступ свим евиденцијама уређаја треба да дозвољавате само апликацијама у које имате поверења. \n\nАко не дозволите овој апликацији да приступа свим евиденцијама уређаја, она и даље може да приступа сопственим евиденцијама. Произвођач уређаја ће можда и даље моћи да приступа неким евиденцијама или информацијама на уређају."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Евиденције уређаја региструју шта се дешава на уређају. Апликације могу да користе те евиденције да би пронашле и решиле проблеме.\n\nНеке евиденције могу да садрже осетљиве информације, па приступ свим евиденцијама уређаја треба да дозвољавате само апликацијама у које имате поверења. \n\nАко не дозволите овој апликацији да приступа свим евиденцијама уређаја, она и даље може да приступа сопственим евиденцијама. Произвођач уређаја ће можда и даље моћи да приступа неким евиденцијама или информацијама на уређају."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Не приказуј поново"</string> <string name="slices_permission_request" msgid="3677129866636153406">"Апликација <xliff:g id="APP_0">%1$s</xliff:g> жели да приказује исечке из апликације <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Измени"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index f24da5cb1149..92e0cd6e9d1c 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -586,16 +586,14 @@ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Använd skärmlåset"</string> <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Fortsätt med hjälp av ditt skärmlås"</string> <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Tryck på sensorn med ett stadigt tryck"</string> - <!-- no translation found for fingerprint_acquired_insufficient (623888149088216458) --> - <skip /> + <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Fingeravtrycket kändes inte igen. Försök igen."</string> <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Rengör fingeravtryckssensorn och försök igen"</string> <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Rengör sensorn och försök igen"</string> <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Tryck hårt på sensorn"</string> <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Du rörde fingret för långsamt. Försök igen."</string> <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Testa ett annat fingeravtryck"</string> <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Det är för ljust"</string> - <!-- no translation found for fingerprint_acquired_power_press (3107864151278434961) --> - <skip /> + <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Tryckning registrerades"</string> <string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Testa att justera fingeravtrycket"</string> <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Flytta fingret lite varje gång"</string> <string-array name="fingerprint_acquired_vendor"> @@ -607,15 +605,12 @@ <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansiktet har autentiserats. Tryck på Bekräfta"</string> <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Det finns ingen maskinvara för fingeravtryck."</string> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Det gick inte att konfigurera fingeravtryck"</string> - <!-- no translation found for fingerprint_error_timeout (7361192266621252164) --> - <skip /> + <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingeravtryckskonfigurering nådde tidsgränsen. Försök igen."</string> <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeravtrycksåtgärden avbröts."</string> <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeravtrycksåtgärden avbröts av användaren."</string> - <!-- no translation found for fingerprint_error_lockout (6626753679019351368) --> - <skip /> + <string name="fingerprint_error_lockout" msgid="6626753679019351368">"För många försök. Använd låsskärmen i stället."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"För många försök. Använd låsskärmen i stället."</string> - <!-- no translation found for fingerprint_error_unable_to_process (2446280592818621224) --> - <skip /> + <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Det gick inte att bearbeta fingeravtrycket. Försök igen."</string> <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Inga fingeravtryck har registrerats."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Enheten har ingen fingeravtryckssensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensorn har tillfälligt inaktiverats."</string> @@ -1256,10 +1251,8 @@ <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Appar startas."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Uppgraderingen är klar."</string> <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du tryckte på av/på-knappen, vilket vanligtvis stänger av skärmen.\n\nTesta att trycka lätt när du konfigurerar fingeravtrycket."</string> - <!-- no translation found for fp_power_button_enrollment_title (6976841690455338563) --> - <skip /> - <!-- no translation found for fp_power_button_enrollment_button_text (3199783266386029200) --> - <skip /> + <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Stäng av skärmen för att avbryta"</string> + <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Stäng av"</string> <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vill du verifiera ditt fingeravtryck?"</string> <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du tryckte på av/på-knappen, vilket vanligtvis stänger av skärmen.\n\nTesta att trycka lätt för att verifiera ditt fingeravtryck."</string> <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Stäng av skärmen"</string> @@ -2057,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Vill du tillåta att <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> får åtkomst till alla enhetsloggar?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Tillåt engångsåtkomst"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Tillåt inte"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"I enhetsloggar registreras vad som händer på enheten. Appar kan använda dessa loggar för att hitta och åtgärda problem.\n\nVissa loggar kan innehålla känsliga uppgifter, så du ska bara bevilja appar du litar på åtkomst till alla enhetsloggar. \n\nEn app har åtkomst till sina egna loggar även om du inte ger den åtkomst till alla enhetsloggar. Enhetens tillverkare kan fortfarande ha åtkomst till vissa loggar eller viss information på enheten."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"I enhetsloggar registreras vad som händer på enheten. Appar kan använda dessa loggar för att hitta och åtgärda problem.\n\nVissa loggar kan innehålla känsliga uppgifter, så du ska bara bevilja appar du litar på åtkomst till alla enhetsloggar. \n\nEn app har åtkomst till sina egna loggar även om du inte ger den åtkomst till alla enhetsloggar. Enhetens tillverkare kan fortfarande ha åtkomst till vissa loggar eller viss information på enheten."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Visa inte igen"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> vill kunna visa bitar av <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Redigera"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index ae2e80178e96..f4f2da0c424e 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Ungependa kuruhusu <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ifikie kumbukumbu zote za kifaa?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Ruhusu ufikiaji wa mara moja"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Usiruhusu"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Kumbukumbu za kifaa zinarekodi kinachofanyika kwenye kifaa chako. Programu zinaweza kutumia kumbukumbu hizi ili kutambua na kurekebisha hitilafu.\n\nBaadhi ya kumbukumbu huenda zikawa na taarifa nyeti, hivyo ruhusu tu programu unazoziamini kufikia kumbukumbu zote za kifaa. \n\nIwapo hutaruhusu programu hii ifikie kumbukumbu zote za kifaa, bado inaweza kufikia kumbukumbu zake yenyewe. Huenda mtengenezaji wa kifaa chako bado akaweza kufikia baadhi ya kumbukumbu au taarifa zilizopo kwenye kifaa chako."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Kumbukumbu za kifaa zinarekodi kinachofanyika kwenye kifaa chako. Programu zinaweza kutumia kumbukumbu hizi ili kutambua na kurekebisha hitilafu.\n\nBaadhi ya kumbukumbu huenda zikawa na taarifa nyeti, hivyo ruhusu tu programu unazoziamini kufikia kumbukumbu zote za kifaa. \n\nIwapo hutaruhusu programu hii ifikie kumbukumbu zote za kifaa, bado inaweza kufikia kumbukumbu zake yenyewe. Huenda mtengenezaji wa kifaa chako bado akaweza kufikia baadhi ya kumbukumbu au taarifa zilizopo kwenye kifaa chako."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Usionyeshe tena"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> inataka kuonyesha vipengee <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Badilisha"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 507bafb0c768..e2502ba1d136 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"சாதனப் பதிவுகள் அனைத்தையும் <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> அணுக அனுமதிக்கவா?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"ஒருமுறை அணுகலை அனுமதி"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"அனுமதிக்க வேண்டாம்"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"உங்கள் சாதனத்தில் நடப்பவற்றைச் சாதனப் பதிவுகள் ரெக்கார்டு செய்யும். சிக்கல்களைக் கண்டறிந்து சரிசெய்ய ஆப்ஸ் இந்தப் பதிவுகளைப் பயன்படுத்தலாம்.\n\nபாதுகாக்கப்பட வேண்டிய தகவல்கள் சில பதிவுகளில் இருக்கக்கூடும் என்பதால் சாதனப் பதிவுகள் அனைத்தையும் அணுக நீங்கள் நம்பும் ஆப்ஸை மட்டும் அனுமதிக்கவும். \n\nசாதனப் பதிவுகள் அனைத்தையும் அணுக இந்த ஆப்ஸை அனுமதிக்கவில்லை என்றாலும் அதற்குச் சொந்தமான பதிவுகளை அதனால் அணுக முடியும். உங்கள் சாதனத்திலுள்ள சில பதிவுகளையோ தகவல்களையோ சாதன உற்பத்தியாளரால் தொடர்ந்து அணுக முடியும்."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"உங்கள் சாதனத்தில் நடப்பவற்றைச் சாதனப் பதிவுகள் ரெக்கார்டு செய்யும். சிக்கல்களைக் கண்டறிந்து சரிசெய்ய ஆப்ஸ் இந்தப் பதிவுகளைப் பயன்படுத்தலாம்.\n\nபாதுகாக்கப்பட வேண்டிய தகவல்கள் சில பதிவுகளில் இருக்கக்கூடும் என்பதால் சாதனப் பதிவுகள் அனைத்தையும் அணுக நீங்கள் நம்பும் ஆப்ஸை மட்டும் அனுமதிக்கவும். \n\nசாதனப் பதிவுகள் அனைத்தையும் அணுக இந்த ஆப்ஸை அனுமதிக்கவில்லை என்றாலும் அதற்குச் சொந்தமான பதிவுகளை அதனால் அணுக முடியும். உங்கள் சாதனத்திலுள்ள சில பதிவுகளையோ தகவல்களையோ சாதன உற்பத்தியாளரால் தொடர்ந்து அணுக முடியும்."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"மீண்டும் காட்டாதே"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_2">%2$s</xliff:g> ஆப்ஸின் விழிப்பூட்டல்களைக் காண்பிக்க, <xliff:g id="APP_0">%1$s</xliff:g> அனுமதி கேட்கிறது"</string> <string name="screenshot_edit" msgid="7408934887203689207">"திருத்து"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 47e3deb394af..c946894bcd54 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"అన్ని పరికర లాగ్లను యాక్సెస్ చేయడానికి <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>ను అనుమతించాలా?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"వన్-టైమ్ యాక్సెస్ను అనుమతించండి"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"అనుమతించవద్దు"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"మీ పరికరంలో జరిగే దాన్ని పరికర లాగ్లు రికార్డ్ చేస్తాయి. సమస్యలను కనుగొని, పరిష్కరించడానికి యాప్లు ఈ లాగ్లను ఉపయోగిస్తాయి.\n\nకొన్ని లాగ్లలో గోప్యమైన సమాచారం ఉండవచ్చు, కాబట్టి మీరు విశ్వసించే యాప్లను మాత్రమే అన్ని పరికర లాగ్లను యాక్సెస్ చేయడానికి అనుమతించండి. \n\nఅన్ని పరికర లాగ్లను యాక్సెస్ చేయడానికి మీరు ఈ యాప్ను అనుమతించకపోతే, అది తన స్వంత లాగ్లను ఇప్పటికి యాక్సెస్ చేయగలదు. మీ పరికర తయారీదారు ఇప్పటికీ మీ పరికరంలో కొన్ని లాగ్లు లేదా సమాచారాన్ని యాక్సెస్ చేయగలరు."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"మీ పరికరంలో జరిగే దాన్ని పరికర లాగ్లు రికార్డ్ చేస్తాయి. సమస్యలను కనుగొని, పరిష్కరించడానికి యాప్లు ఈ లాగ్లను ఉపయోగిస్తాయి.\n\nకొన్ని లాగ్లలో గోప్యమైన సమాచారం ఉండవచ్చు, కాబట్టి మీరు విశ్వసించే యాప్లను మాత్రమే అన్ని పరికర లాగ్లను యాక్సెస్ చేయడానికి అనుమతించండి. \n\nఅన్ని పరికర లాగ్లను యాక్సెస్ చేయడానికి మీరు ఈ యాప్ను అనుమతించకపోతే, అది తన స్వంత లాగ్లను ఇప్పటికి యాక్సెస్ చేయగలదు. మీ పరికర తయారీదారు ఇప్పటికీ మీ పరికరంలో కొన్ని లాగ్లు లేదా సమాచారాన్ని యాక్సెస్ చేయగలరు."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"మళ్లీ చూపవద్దు"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> <xliff:g id="APP_2">%2$s</xliff:g> స్లైస్లను చూపించాలనుకుంటోంది"</string> <string name="screenshot_edit" msgid="7408934887203689207">"ఎడిట్ చేయండి"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 2d2e17260a8f..5d983797962f 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"อนุญาตให้ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> เข้าถึงบันทึกทั้งหมดของอุปกรณ์ใช่ไหม"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"อนุญาตสิทธิ์เข้าถึงแบบครั้งเดียว"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"ไม่อนุญาต"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"บันทึกของอุปกรณ์เก็บข้อมูลสิ่งที่เกิดขึ้นในอุปกรณ์ แอปสามารถใช้บันทึกเหล่านี้เพื่อค้นหาและแก้ไขปัญหา\n\nบันทึกบางรายการอาจมีข้อมูลที่ละเอียดอ่อน คุณจึงควรอนุญาตเฉพาะแอปที่เชื่อถือได้ให้เข้าถึงบันทึกทั้งหมดของอุปกรณ์ \n\nหากคุณไม่อนุญาตให้แอปนี้เข้าถึงบันทึกทั้งหมดของอุปกรณ์ แอปจะยังเข้าถึงบันทึกของตัวเองได้อยู่ ผู้ผลิตอุปกรณ์อาจยังเข้าถึงบันทึกหรือข้อมูลบางรายการในอุปกรณ์ของคุณได้"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"บันทึกของอุปกรณ์เก็บข้อมูลสิ่งที่เกิดขึ้นในอุปกรณ์ แอปสามารถใช้บันทึกเหล่านี้เพื่อค้นหาและแก้ไขปัญหา\n\nบันทึกบางรายการอาจมีข้อมูลที่ละเอียดอ่อน คุณจึงควรอนุญาตเฉพาะแอปที่เชื่อถือได้ให้เข้าถึงบันทึกทั้งหมดของอุปกรณ์ \n\nหากคุณไม่อนุญาตให้แอปนี้เข้าถึงบันทึกทั้งหมดของอุปกรณ์ แอปจะยังเข้าถึงบันทึกของตัวเองได้อยู่ ผู้ผลิตอุปกรณ์อาจยังเข้าถึงบันทึกหรือข้อมูลบางรายการในอุปกรณ์ของคุณได้"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"ไม่ต้องแสดงอีก"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ต้องการแสดงส่วนต่างๆ ของ <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"แก้ไข"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 298239ec4b18..5e322147a3ee 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Payagan ang <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> na i-access ang lahat ng log ng device?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Payagan ang isang beses na pag-access"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Huwag payagan"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Nire-record ng mga log ng device kung ano ang nangyayari sa iyong device. Magagamit ng mga app ang mga log na ito para maghanap at mag-ayos ng mga isyu.\n\nPosibleng maglaman ang ilang log ng sensitibong impormasyon, kaya ang mga app lang na pinagkakatiwalaan mo ang payagang maka-access sa lahat ng log ng device. \n\nKung hindi mo papayagan ang app na ito na i-access ang lahat ng log ng device, maa-access pa rin nito ang mga sarili nitong log. Posible pa ring ma-access ng manufacturer ng iyong device ang ilang log o impormasyon sa device mo."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Nire-record ng mga log ng device kung ano ang nangyayari sa iyong device. Magagamit ng mga app ang mga log na ito para maghanap at mag-ayos ng mga isyu.\n\nPosibleng maglaman ang ilang log ng sensitibong impormasyon, kaya ang mga app lang na pinagkakatiwalaan mo ang payagang maka-access sa lahat ng log ng device. \n\nKung hindi mo papayagan ang app na ito na i-access ang lahat ng log ng device, maa-access pa rin nito ang mga sarili nitong log. Posible pa ring ma-access ng manufacturer ng iyong device ang ilang log o impormasyon sa device mo."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Huwag ipakita ulit"</string> <string name="slices_permission_request" msgid="3677129866636153406">"Gustong ipakita ng <xliff:g id="APP_0">%1$s</xliff:g> ang mga slice ng <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"I-edit"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 7e308bdf5675..c9c2d113121f 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> uygulamasının tüm cihaz günlüklerine erişmesine izin verilsin mi?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Tek seferlik erişim izni ver"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"İzin verme"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Cihaz günlükleri, cihazınızda olanları kaydeder. Uygulamalar, sorunları bulup düzeltmek için bu günlükleri kullanabilir.\n\nBazı günlükler hassas bilgiler içerebileceği için yalnızca güvendiğiniz uygulamaların tüm cihaz günlüklerine erişmesine izin verin. \n\nBu uygulamanın tüm cihaz günlüklerine erişmesine izin vermeseniz de kendi günlüklerine erişmeye devam edebilir. Ayrıca, cihaz üreticiniz de cihazınızdaki bazı günlüklere veya bilgilere erişmeye devam edebilir."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Cihaz günlükleri, cihazınızda olanları kaydeder. Uygulamalar, sorunları bulup düzeltmek için bu günlükleri kullanabilir.\n\nBazı günlükler hassas bilgiler içerebileceği için yalnızca güvendiğiniz uygulamaların tüm cihaz günlüklerine erişmesine izin verin. \n\nBu uygulamanın tüm cihaz günlüklerine erişmesine izin vermeseniz de kendi günlüklerine erişmeye devam edebilir. Ayrıca, cihaz üreticiniz de cihazınızdaki bazı günlüklere veya bilgilere erişmeye devam edebilir."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Bir daha gösterme"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> uygulaması, <xliff:g id="APP_2">%2$s</xliff:g> dilimlerini göstermek istiyor"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Düzenle"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index f2c422dab1ae..163c629dbaa9 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -588,16 +588,14 @@ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Доступ розблокуванням екрана"</string> <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Щоб продовжити, введіть дані для розблокування екрана"</string> <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Міцно притисніть палець до сканера"</string> - <!-- no translation found for fingerprint_acquired_insufficient (623888149088216458) --> - <skip /> + <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Відбиток пальця не розпізнано. Повторіть спробу."</string> <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Очистьте сканер відбитків пальців і повторіть спробу"</string> <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Очистьте сканер і повторіть спробу"</string> <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Міцно притисніть палець до сканера"</string> <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Ви провели пальцем надто повільно. Повторіть спробу."</string> <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Спробуйте інший відбиток пальця"</string> <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Надто яскраво"</string> - <!-- no translation found for fingerprint_acquired_power_press (3107864151278434961) --> - <skip /> + <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Виявлено натискання кнопки живлення"</string> <string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Спробуйте відкоригувати відбиток пальця"</string> <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Щоразу трохи змінюйте положення пальця"</string> <string-array name="fingerprint_acquired_vendor"> @@ -609,15 +607,12 @@ <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Обличчя автентифіковано. Натисніть \"Підтвердити\""</string> <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Сканер відбитків пальців недоступний."</string> <string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не вдалося створити відбиток пальця"</string> - <!-- no translation found for fingerprint_error_timeout (7361192266621252164) --> - <skip /> + <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Час очікування для налаштування відбитка пальця минув. Повторіть спробу."</string> <string name="fingerprint_error_canceled" msgid="540026881380070750">"Дію з відбитком пальця скасовано."</string> <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Користувач скасував дію з відбитком пальця."</string> - <!-- no translation found for fingerprint_error_lockout (6626753679019351368) --> - <skip /> + <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Забагато спроб. Використайте натомість розблокування екрана."</string> <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Забагато спроб. Використайте натомість розблокування екрана."</string> - <!-- no translation found for fingerprint_error_unable_to_process (2446280592818621224) --> - <skip /> + <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не вдалось обробити відбиток пальця. Повторіть спробу."</string> <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Відбитки пальців не зареєстровано."</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На цьому пристрої немає сканера відбитків пальців."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик тимчасово вимкнено."</string> @@ -1088,7 +1083,7 @@ <string name="searchview_description_search" msgid="1045552007537359343">"Пошук"</string> <string name="searchview_description_query" msgid="7430242366971716338">"Пошуковий запит"</string> <string name="searchview_description_clear" msgid="1989371719192982900">"Очистити запит"</string> - <string name="searchview_description_submit" msgid="6771060386117334686">"Наіслати запит"</string> + <string name="searchview_description_submit" msgid="6771060386117334686">"Надіслати запит"</string> <string name="searchview_description_voice" msgid="42360159504884679">"Голосовий пошук"</string> <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Увімкнути дослідження дотиком?"</string> <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> хоче ввімкнути функцію дослідження дотиком. Увімкнувши функцію дослідження дотиком, можна чути або бачити опис елемента, розташованого під вашим пальцем, або виконувати жести для взаємодії з планшетним ПК."</string> @@ -1258,10 +1253,8 @@ <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск програм."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Завершення завантаження."</string> <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Ви натиснули кнопку живлення – зазвичай після цього вимикається екран.\n\nЩоб зареєструвати відбиток пальця, спробуйте лише злегка торкнутися датчика."</string> - <!-- no translation found for fp_power_button_enrollment_title (6976841690455338563) --> - <skip /> - <!-- no translation found for fp_power_button_enrollment_button_text (3199783266386029200) --> - <skip /> + <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Щоб завершити, вимкніть екран"</string> + <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Вимкнути"</string> <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Продовжити підтвердження відбитка?"</string> <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Ви натиснули кнопку живлення – зазвичай після цього вимикається екран.\n\nЩоб підтвердити відбиток пальця, спробуйте лише злегка торкнутися датчика."</string> <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Вимкнути екран"</string> @@ -2059,7 +2052,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Надати додатку <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> доступ до всіх журналів пристрою?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Надати доступ лише цього разу"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Не дозволяти"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"У журналах пристрою реєструється все, що відбувається на ньому. За допомогою цих журналів додатки можуть виявляти й усувати проблеми.\n\nДеякі журнали можуть містити конфіденційні дані, тому надавати доступ до всіх журналів пристрою слід лише надійним додаткам. \n\nЯкщо додаток не має доступу до всіх журналів пристрою, він усе одно може використовувати власні журнали. Виробник вашого пристрою все одно може використовувати деякі журнали чи інформацію на ньому."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"У журналах пристрою реєструється все, що відбувається на ньому. За допомогою цих журналів додатки можуть виявляти й усувати проблеми.\n\nДеякі журнали можуть містити конфіденційні дані, тому надавати доступ до всіх журналів пристрою слід лише надійним додаткам. \n\nЯкщо додаток не має доступу до всіх журналів пристрою, він усе одно може використовувати власні журнали. Виробник вашого пристрою все одно може використовувати деякі журнали чи інформацію на ньому."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Більше не показувати"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> хоче показати фрагменти додатка <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Редагувати"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 2520a94da49a..94f5ab525cd4 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> کو آلے کے تمام لاگز تک رسائی کی اجازت دیں؟"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"یک وقتی رسائی کی اجازت دیں"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"اجازت نہ دیں"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"آپ کے آلے پر جو ہوتا ہے آلے کے لاگز اسے ریکارڈ کر لیتے ہیں۔ ایپس ان لاگز کا استعمال مسائل کو تلاش کرنے اور ان کو حل کرنے کے لیے کر سکتی ہیں۔\n\nکچھ لاگز میں حساس معلومات شامل ہو سکتی ہیں، اس لیے صرف اپنے بھروسے مند ایپس کو ہی آلے کے تمام لاگز تک رسائی کی اجازت دیں۔ \n\nاگر آپ اس ایپ کو آلے کے تمام لاگز تک رسائی کی اجازت نہیں دیتے ہیں تب بھی یہ اپنے لاگز تک رسائی حاصل کر سکتی ہے۔ آپ کے آلے کا مینوفیکچرر اب بھی آپ کے آلے پر کچھ لاگز یا معلومات تک رسائی حاصل کر سکتا ہے۔"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"آپ کے آلے پر جو ہوتا ہے آلے کے لاگز اسے ریکارڈ کر لیتے ہیں۔ ایپس ان لاگز کا استعمال مسائل کو تلاش کرنے اور ان کو حل کرنے کے لیے کر سکتی ہیں۔\n\nکچھ لاگز میں حساس معلومات شامل ہو سکتی ہیں، اس لیے صرف اپنے بھروسے مند ایپس کو ہی آلے کے تمام لاگز تک رسائی کی اجازت دیں۔ \n\nاگر آپ اس ایپ کو آلے کے تمام لاگز تک رسائی کی اجازت نہیں دیتے ہیں تب بھی یہ اپنے لاگز تک رسائی حاصل کر سکتی ہے۔ آپ کے آلے کا مینوفیکچرر اب بھی آپ کے آلے پر کچھ لاگز یا معلومات تک رسائی حاصل کر سکتا ہے۔"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"دوبارہ نہ دکھائیں"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> <xliff:g id="APP_2">%2$s</xliff:g> کے سلائسز دکھانا چاہتی ہے"</string> <string name="screenshot_edit" msgid="7408934887203689207">"ترمیم کریں"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index ba7138fee359..92b231526354 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ilovasining qurilmadagi barcha jurnallarga kirishiga ruxsat berilsinmi?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Bir matalik foydalanishga ruxsat berish"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Rad etish"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Qurilma jurnaliga qurilma bilan yuz bergan hodisalar qaydlari yoziladi. Ilovalar bu jurnal qaydlari yordamida muammolarni topishi va bartaraf qilishi mumkin.\n\nAyrim jurnal qaydlarida maxfiy axborotlar yozilishi mumkin, shu sababli qurilmadagi barcha jurnal qaydlariga ruxsatni faqat ishonchli ilovalarga bering. \n\nBu ilovaga qurilmadagi barcha jurnal qaydlariga ruxsat berilmasa ham, u oʻzining jurnalini ocha oladi. Qurilma ishlab chiqaruvchisi ham ayrim jurnallar yoki qurilma haqidagi axborotlarni ocha oladi."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Qurilma jurnaliga qurilma bilan yuz bergan hodisalar qaydlari yoziladi. Ilovalar bu jurnal qaydlari yordamida muammolarni topishi va bartaraf qilishi mumkin.\n\nAyrim jurnal qaydlarida maxfiy axborotlar yozilishi mumkin, shu sababli qurilmadagi barcha jurnal qaydlariga ruxsatni faqat ishonchli ilovalarga bering. \n\nBu ilovaga qurilmadagi barcha jurnal qaydlariga ruxsat berilmasa ham, u oʻzining jurnalini ocha oladi. Qurilma ishlab chiqaruvchisi ham ayrim jurnallar yoki qurilma haqidagi axborotlarni ocha oladi."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Boshqa chiqmasin"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> ilovasi <xliff:g id="APP_2">%2$s</xliff:g> ilovasidan fragmentlar ko‘rsatish uchun ruxsat so‘ramoqda"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Tahrirlash"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index dc4c00570c49..a45b1a43b3bb 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Cho phép <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> truy cập vào tất cả các nhật ký thiết bị?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Cho phép truy cập một lần"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Không cho phép"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Nhật ký thiết bị ghi lại những hoạt động diễn ra trên thiết bị. Các ứng dụng có thể dùng nhật ký này để tìm và khắc phục sự cố.\n\nMột số nhật ký có thể chứa thông tin nhạy cảm, vì vậy, bạn chỉ nên cấp quyền truy cập vào toàn bộ nhật ký thiết bị cho những ứng dụng mà mình tin cậy. \n\nNếu bạn không cho phép ứng dụng này truy cập vào toàn bộ nhật ký thiết bị, thì ứng dụng vẫn có thể truy cập vào nhật ký của chính nó. Nhà sản xuất thiết bị vẫn có thể truy cập vào một số nhật ký hoặc thông tin trên thiết bị của bạn."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Nhật ký thiết bị ghi lại những hoạt động diễn ra trên thiết bị. Các ứng dụng có thể dùng nhật ký này để tìm và khắc phục sự cố.\n\nMột số nhật ký có thể chứa thông tin nhạy cảm, vì vậy, bạn chỉ nên cấp quyền truy cập vào toàn bộ nhật ký thiết bị cho những ứng dụng mà mình tin cậy. \n\nNếu bạn không cho phép ứng dụng này truy cập vào toàn bộ nhật ký thiết bị, thì ứng dụng vẫn có thể truy cập vào nhật ký của chính nó. Nhà sản xuất thiết bị vẫn có thể truy cập vào một số nhật ký hoặc thông tin trên thiết bị của bạn."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Không hiện lại"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> muốn hiển thị các lát của <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Chỉnh sửa"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index f830731adf8e..023d9d1f67d2 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"允许“<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>”访问所有设备日志吗?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"允许访问一次"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"不允许"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"设备日志会记录设备上发生的活动。应用可以使用这些日志查找和修复问题。\n\n部分日志可能包含敏感信息,因此请仅允许您信任的应用访问所有设备日志。\n\n如果您不授予此应用访问所有设备日志的权限,它仍然可以访问自己的日志。您的设备制造商可能仍然能够访问设备上的部分日志或信息。"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"设备日志会记录设备上发生的活动。应用可以使用这些日志查找和修复问题。\n\n部分日志可能包含敏感信息,因此请仅允许您信任的应用访问所有设备日志。\n\n如果您不授予此应用访问所有设备日志的权限,它仍然可以访问自己的日志。您的设备制造商可能仍然能够访问设备上的部分日志或信息。"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"不再显示"</string> <string name="slices_permission_request" msgid="3677129866636153406">"“<xliff:g id="APP_0">%1$s</xliff:g>”想要显示“<xliff:g id="APP_2">%2$s</xliff:g>”图块"</string> <string name="screenshot_edit" msgid="7408934887203689207">"编辑"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 3ffffdc444b2..dae4c35e6393 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"要允許「<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>」存取所有裝置記錄嗎?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"允許存取一次"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"不允許"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"裝置記錄會記下裝置上的活動。應用程式可透過這些記錄找出並修正問題。\n\n部分記錄可能包含敏感資料,因此請只允許信任的應用程式存取所有裝置記錄。\n\n如果不允許此應用程式存取所有裝置記錄,此應用程式仍能存取自己的記錄,且裝置製造商可能仍可存取裝置上的部分記錄或資料。"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"裝置記錄會記下裝置上的活動。應用程式可透過這些記錄找出並修正問題。\n\n部分記錄可能包含敏感資料,因此請只允許信任的應用程式存取所有裝置記錄。\n\n如果不允許此應用程式存取所有裝置記錄,此應用程式仍能存取自己的記錄,且裝置製造商可能仍可存取裝置上的部分記錄或資料。"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"不要再顯示"</string> <string name="slices_permission_request" msgid="3677129866636153406">"「<xliff:g id="APP_0">%1$s</xliff:g>」想顯示「<xliff:g id="APP_2">%2$s</xliff:g>」的快訊"</string> <string name="screenshot_edit" msgid="7408934887203689207">"編輯"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 9698053f1320..3da603d33e1e 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"要允許「<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>」存取所有裝置記錄嗎?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"允許一次性存取"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"不允許"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"系統會透過裝置記錄記下裝置上的活動。應用程式可以根據這些記錄找出問題並進行修正。\n\n某些記錄可能含有機密資訊,因此請勿讓不信任的應用程式存取所有裝置記錄。\n\n即使你不允許這個應用程式存取所有裝置記錄,這個應用程式仍能存取自己的記錄,而且裝置製造商或許仍可存取裝置的某些記錄或資訊。"</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"系統會透過裝置記錄記下裝置上的活動。應用程式可以根據這些記錄找出問題並進行修正。\n\n某些記錄可能含有機密資訊,因此請勿讓不信任的應用程式存取所有裝置記錄。\n\n即使你不允許這個應用程式存取所有裝置記錄,這個應用程式仍能存取自己的記錄,而且裝置製造商或許仍可存取裝置的某些記錄或資訊。"</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"不要再顯示"</string> <string name="slices_permission_request" msgid="3677129866636153406">"「<xliff:g id="APP_0">%1$s</xliff:g>」想要顯示「<xliff:g id="APP_2">%2$s</xliff:g>」的區塊"</string> <string name="screenshot_edit" msgid="7408934887203689207">"編輯"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 539c02a3adad..9028854b506a 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -2050,7 +2050,9 @@ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Vumela i-<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> ukuba ifinyelele wonke amalogu edivayisi?"</string> <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Vumela ukufinyelela kwesikhathi esisodwa"</string> <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ungavumeli"</string> - <string name="log_access_confirmation_body" msgid="1806692062668620735">"Amalogu edivayisi arekhoda okwenzekayo kudivayisi yakho. Ama-app angasebenzisa lawa malogu ukuze athole futhi alungise izinkinga.\n\nAmanye amalogu angase aqukathe ulwazi olubucayi, ngakho vumela ama-app owathembayo kuphela ukuthi afinyelele wonke amalogu edivayisi. \n\nUma ungayivumeli le app ukuthi ifinyelele wonke amalogu wedivayisi, isengakwazi ukufinyelela amalogu wayo. Umkhiqizi wedivayisi yakho usengakwazi ukufinyelela amanye amalogu noma ulwazi kudivayisi yakho."</string> + <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Amalogu edivayisi arekhoda okwenzekayo kudivayisi yakho. Ama-app angasebenzisa lawa malogu ukuze athole futhi alungise izinkinga.\n\nAmanye amalogu angase aqukathe ulwazi olubucayi, ngakho vumela ama-app owathembayo kuphela ukuthi afinyelele wonke amalogu edivayisi. \n\nUma ungayivumeli le app ukuthi ifinyelele wonke amalogu wedivayisi, isengakwazi ukufinyelela amalogu wayo. Umkhiqizi wedivayisi yakho usengakwazi ukufinyelela amanye amalogu noma ulwazi kudivayisi yakho."</string> + <!-- no translation found for log_access_confirmation_body (7379536536425265262) --> + <skip /> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ungabonisi futhi"</string> <string name="slices_permission_request" msgid="3677129866636153406">"I-<xliff:g id="APP_0">%1$s</xliff:g> ifuna ukubonisa izingcezu ze-<xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Hlela"</string> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6ec98e82211c..5763345aba4d 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5755,10 +5755,21 @@ <string name="log_access_confirmation_deny">Don\u2019t allow</string> <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]--> - <string name="log_access_confirmation_body">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs. + <string name="log_access_confirmation_body" product="default">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device. </string> + <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]--> + <string name="log_access_confirmation_body" product="tv">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs. + \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device.\n\nLearn more at g.co/android/devicelogs. + </string> + + <!-- Learn more URL for the log access confirmation dialog. [DO NOT TRANSLATE]--> + <string name="log_access_confirmation_learn_more" product="default" translatable="false"><a href="https://support.google.com/android?p=system_logs#topic=7313011">Learn more</a></string> + + <!-- Learn more URL for the log access confirmation dialog. [DO NOT TRANSLATE]--> + <string name="log_access_confirmation_learn_more" product="tv" translatable="false"></string> + <!-- Privacy notice do not show [CHAR LIMIT=20] --> <string name="log_access_do_not_show_again">Don\u2019t show again</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 546bb21df2a6..6e574bd788a9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3925,8 +3925,10 @@ <java-symbol type="string" name="log_access_confirmation_deny" /> <java-symbol type="string" name="log_access_confirmation_title" /> <java-symbol type="string" name="log_access_confirmation_body" /> + <java-symbol type="string" name="log_access_confirmation_learn_more" /> <java-symbol type="layout" name="log_access_user_consent_dialog_permission" /> <java-symbol type="id" name="log_access_dialog_title" /> + <java-symbol type="id" name="log_access_dialog_body" /> <java-symbol type="id" name="log_access_dialog_allow_button" /> <java-symbol type="id" name="log_access_dialog_deny_button" /> diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java index 4c247427ef8f..9e39e13265bd 100644 --- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java +++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java @@ -16,11 +16,12 @@ package android.hardware.devicestate; +import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -36,7 +37,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import org.mockito.Mockito; import java.util.HashSet; import java.util.Set; @@ -59,6 +59,7 @@ public final class DeviceStateManagerGlobalTest { public void setUp() { mService = new TestDeviceStateManagerService(); mDeviceStateManagerGlobal = new DeviceStateManagerGlobal(mService); + assertFalse(mService.mCallbacks.isEmpty()); } @Test @@ -79,8 +80,8 @@ public final class DeviceStateManagerGlobalTest { verify(callback2).onBaseStateChanged(eq(mService.getBaseState())); verify(callback2).onStateChanged(eq(mService.getMergedState())); - Mockito.reset(callback1); - Mockito.reset(callback2); + reset(callback1); + reset(callback2); // Change the supported states and verify callback mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE }); @@ -88,8 +89,8 @@ public final class DeviceStateManagerGlobalTest { verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedStates())); mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE }); - Mockito.reset(callback1); - Mockito.reset(callback2); + reset(callback1); + reset(callback2); // Change the base state and verify callback mService.setBaseState(OTHER_DEVICE_STATE); @@ -98,8 +99,8 @@ public final class DeviceStateManagerGlobalTest { verify(callback2).onBaseStateChanged(eq(mService.getBaseState())); verify(callback2).onStateChanged(eq(mService.getMergedState())); - Mockito.reset(callback1); - Mockito.reset(callback2); + reset(callback1); + reset(callback2); // Change the requested state and verify callback DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build(); @@ -120,7 +121,7 @@ public final class DeviceStateManagerGlobalTest { verify(callback).onSupportedStatesChanged(eq(mService.getSupportedStates())); verify(callback).onBaseStateChanged(eq(mService.getBaseState())); verify(callback).onStateChanged(eq(mService.getMergedState())); - Mockito.reset(callback); + reset(callback); mDeviceStateManagerGlobal.unregisterDeviceStateCallback(callback); @@ -130,33 +131,86 @@ public final class DeviceStateManagerGlobalTest { } @Test - public void submittingRequestRegistersCallback() { - assertTrue(mService.mCallbacks.isEmpty()); + public void submitRequest() { + DeviceStateCallback callback = mock(DeviceStateCallback.class); + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, + ConcurrentUtils.DIRECT_EXECUTOR); - DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build(); + verify(callback).onStateChanged(eq(mService.getBaseState())); + reset(callback); + + DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build(); mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */); - assertFalse(mService.mCallbacks.isEmpty()); + verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE)); + reset(callback); + + mDeviceStateManagerGlobal.cancelStateRequest(); + + verify(callback).onStateChanged(eq(mService.getBaseState())); } @Test - public void submitRequest() { + public void submitBaseStateOverrideRequest() { DeviceStateCallback callback = mock(DeviceStateCallback.class); mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); + verify(callback).onBaseStateChanged(eq(mService.getBaseState())); verify(callback).onStateChanged(eq(mService.getBaseState())); - Mockito.reset(callback); + reset(callback); DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build(); - mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */); + mDeviceStateManagerGlobal.requestBaseStateOverride(request, null /* executor */, + null /* callback */); + verify(callback).onBaseStateChanged(eq(OTHER_DEVICE_STATE)); verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE)); - Mockito.reset(callback); + reset(callback); - mDeviceStateManagerGlobal.cancelStateRequest(); + mDeviceStateManagerGlobal.cancelBaseStateOverride(); + + verify(callback).onBaseStateChanged(eq(mService.getBaseState())); + verify(callback).onStateChanged(eq(mService.getBaseState())); + } + + @Test + public void submitBaseAndEmulatedStateOverride() { + DeviceStateCallback callback = mock(DeviceStateCallback.class); + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, + ConcurrentUtils.DIRECT_EXECUTOR); + verify(callback).onBaseStateChanged(eq(mService.getBaseState())); verify(callback).onStateChanged(eq(mService.getBaseState())); + reset(callback); + + DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build(); + mDeviceStateManagerGlobal.requestBaseStateOverride(request, null /* executor */, + null /* callback */); + + verify(callback).onBaseStateChanged(eq(OTHER_DEVICE_STATE)); + verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE)); + assertEquals(OTHER_DEVICE_STATE, mService.getBaseState()); + reset(callback); + + DeviceStateRequest secondRequest = DeviceStateRequest.newBuilder( + DEFAULT_DEVICE_STATE).build(); + + mDeviceStateManagerGlobal.requestState(secondRequest, null, null); + + assertEquals(OTHER_DEVICE_STATE, mService.getBaseState()); + verify(callback).onStateChanged(eq(DEFAULT_DEVICE_STATE)); + reset(callback); + + mDeviceStateManagerGlobal.cancelStateRequest(); + + verify(callback).onStateChanged(OTHER_DEVICE_STATE); + reset(callback); + + mDeviceStateManagerGlobal.cancelBaseStateOverride(); + + verify(callback).onBaseStateChanged(DEFAULT_DEVICE_STATE); + verify(callback).onStateChanged(DEFAULT_DEVICE_STATE); } @Test @@ -169,7 +223,7 @@ public final class DeviceStateManagerGlobalTest { callback /* callback */); verify(callback).onRequestActivated(eq(request)); - Mockito.reset(callback); + reset(callback); mDeviceStateManagerGlobal.cancelStateRequest(); @@ -203,13 +257,16 @@ public final class DeviceStateManagerGlobalTest { private int[] mSupportedStates = new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE }; private int mBaseState = DEFAULT_DEVICE_STATE; private Request mRequest; + private Request mBaseStateRequest; private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>(); private DeviceStateInfo getInfo() { + final int mergedBaseState = mBaseStateRequest == null + ? mBaseState : mBaseStateRequest.state; final int mergedState = mRequest == null - ? mBaseState : mRequest.state; - return new DeviceStateInfo(mSupportedStates, mBaseState, mergedState); + ? mergedBaseState : mRequest.state; + return new DeviceStateInfo(mSupportedStates, mergedBaseState, mergedState); } private void notifyDeviceStateInfoChanged() { @@ -238,7 +295,7 @@ public final class DeviceStateManagerGlobalTest { try { callback.onDeviceStateInfoChanged(getInfo()); } catch (RemoteException e) { - // Do nothing. Should never happen. + e.rethrowFromSystemServer(); } } @@ -249,7 +306,7 @@ public final class DeviceStateManagerGlobalTest { try { callback.onRequestCanceled(mRequest.token); } catch (RemoteException e) { - // Do nothing. Should never happen. + e.rethrowFromSystemServer(); } } } @@ -262,7 +319,7 @@ public final class DeviceStateManagerGlobalTest { try { callback.onRequestActive(token); } catch (RemoteException e) { - // Do nothing. Should never happen. + e.rethrowFromSystemServer(); } } } @@ -275,7 +332,46 @@ public final class DeviceStateManagerGlobalTest { try { callback.onRequestCanceled(token); } catch (RemoteException e) { - // Do nothing. Should never happen. + e.rethrowFromSystemServer(); + } + } + notifyDeviceStateInfoChanged(); + } + + @Override + public void requestBaseStateOverride(IBinder token, int state, int flags) { + if (mBaseStateRequest != null) { + for (IDeviceStateManagerCallback callback : mCallbacks) { + try { + callback.onRequestCanceled(mBaseStateRequest.token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + } + + final Request request = new Request(token, state, flags); + mBaseStateRequest = request; + notifyDeviceStateInfoChanged(); + + for (IDeviceStateManagerCallback callback : mCallbacks) { + try { + callback.onRequestActive(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + } + + @Override + public void cancelBaseStateOverride() throws RemoteException { + IBinder token = mBaseStateRequest.token; + mBaseStateRequest = null; + for (IDeviceStateManagerCallback callback : mCallbacks) { + try { + callback.onRequestCanceled(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); } } notifyDeviceStateInfoChanged(); @@ -296,7 +392,7 @@ public final class DeviceStateManagerGlobalTest { } public int getBaseState() { - return mBaseState; + return getInfo().baseState; } public int getMergedState() { diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index ce6df2068525..b1ecb43f9a23 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -3091,6 +3091,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/WindowStateAnimator.java" }, + "829869827": { + "message": "Cannot launch dream activity due to invalid state. dreaming: %b packageName: %s", + "level": "ERROR", + "group": "WM_DEBUG_DREAM", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "835814848": { "message": "%s", "level": "INFO", @@ -4123,6 +4129,12 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/TaskOrganizerController.java" }, + "1918771553": { + "message": "Dream packageName does not match active dream. Package %s does not match %s or %s", + "level": "ERROR", + "group": "WM_DEBUG_DREAM", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "1921821199": { "message": "Preserving %s until the new one is added", "level": "VERBOSE", @@ -4365,6 +4377,9 @@ "WM_DEBUG_DRAW": { "tag": "WindowManager" }, + "WM_DEBUG_DREAM": { + "tag": "WindowManager" + }, "WM_DEBUG_FOCUS": { "tag": "WindowManager" }, diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 7e9c4189dabb..fb0a9db6a20b 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -41,7 +41,7 @@ public class WindowExtensionsImpl implements WindowExtensions { // TODO(b/241126279) Introduce constants to better version functionality @Override public int getVendorApiLevel() { - return 2; + return 1; } /** diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 626e0d990a6d..18712aed1be6 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -432,7 +432,7 @@ class TaskFragmentContainer { // In case we have requested to reparent the activity to another container (as // pendingAppeared), we don't want to finish it with this container. && mController.getContainerWithActivity(activity) == this) { - activity.finish(); + wct.finishActivity(activity.getActivityToken()); } } @@ -457,7 +457,7 @@ class TaskFragmentContainer { || controller.shouldRetainAssociatedActivity(this, activity)) { continue; } - activity.finish(); + wct.finishActivity(activity.getActivityToken()); } mActivitiesToFinishOnExit.clear(); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 179696a063b1..25d034756265 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -207,7 +207,7 @@ public class SplitControllerTest { verify(mSplitPresenter, never()).deleteTaskFragment(any(), any()); verify(mSplitController).removeContainer(tf); - verify(mActivity, never()).finish(); + verify(mTransaction, never()).finishActivity(any()); } @Test @@ -1004,9 +1004,9 @@ public class SplitControllerTest { assertTrue(primaryContainer.isFinished()); assertTrue(secondaryContainer0.isFinished()); assertTrue(secondaryContainer1.isFinished()); - verify(mActivity).finish(); - verify(secondaryActivity0).finish(); - verify(secondaryActivity1).finish(); + verify(mTransaction).finishActivity(mActivity.getActivityToken()); + verify(mTransaction).finishActivity(secondaryActivity0.getActivityToken()); + verify(mTransaction).finishActivity(secondaryActivity1.getActivityToken()); assertTrue(taskContainer.mContainers.isEmpty()); assertTrue(taskContainer.mSplitContainers.isEmpty()); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java index 73428a2dc800..35415d816d8b 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java @@ -107,30 +107,29 @@ public class TaskFragmentContainerTest { final TaskFragmentContainer container = new TaskFragmentContainer(mActivity, null /* pendingAppearedIntent */, taskContainer, mController); doReturn(container).when(mController).getContainerWithActivity(mActivity); - final WindowContainerTransaction wct = new WindowContainerTransaction(); // Only remove the activity, but not clear the reference until appeared. - container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController); + container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController); - verify(mActivity).finish(); + verify(mTransaction).finishActivity(mActivity.getActivityToken()); verify(mPresenter, never()).deleteTaskFragment(any(), any()); verify(mController, never()).removeContainer(any()); // Calling twice should not finish activity again. - clearInvocations(mActivity); - container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController); + clearInvocations(mTransaction); + container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController); - verify(mActivity, never()).finish(); + verify(mTransaction, never()).finishActivity(any()); verify(mPresenter, never()).deleteTaskFragment(any(), any()); verify(mController, never()).removeContainer(any()); // Remove all references after the container has appeared in server. doReturn(new ArrayList<>()).when(mInfo).getActivities(); container.setInfo(mTransaction, mInfo); - container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController); + container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController); - verify(mActivity, never()).finish(); - verify(mPresenter).deleteTaskFragment(wct, container.getTaskFragmentToken()); + verify(mTransaction, never()).finishActivity(any()); + verify(mPresenter).deleteTaskFragment(mTransaction, container.getTaskFragmentToken()); verify(mController).removeContainer(container); } @@ -150,7 +149,7 @@ public class TaskFragmentContainerTest { // The activity is requested to be reparented, so don't finish it. container0.finish(true /* shouldFinishDependent */, mPresenter, wct, mController); - verify(mActivity, never()).finish(); + verify(mTransaction, never()).finishActivity(any()); verify(mPresenter).deleteTaskFragment(wct, container0.getTaskFragmentToken()); verify(mController).removeContainer(container0); } diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml index 93fc93f29d50..f790787c0eb4 100644 --- a/libs/WindowManager/Shell/res/values-ro/strings.xml +++ b/libs/WindowManager/Shell/res/values-ro/strings.xml @@ -18,14 +18,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="pip_phone_close" msgid="5783752637260411309">"Închide"</string> - <string name="pip_phone_expand" msgid="2579292903468287504">"Extindeți"</string> + <string name="pip_phone_expand" msgid="2579292903468287504">"Extinde"</string> <string name="pip_phone_settings" msgid="5468987116750491918">"Setări"</string> - <string name="pip_phone_enter_split" msgid="7042877263880641911">"Accesați ecranul împărțit"</string> + <string name="pip_phone_enter_split" msgid="7042877263880641911">"Accesează ecranul împărțit"</string> <string name="pip_menu_title" msgid="5393619322111827096">"Meniu"</string> <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> este în modul picture-in-picture"</string> <string name="pip_notification_message" msgid="8854051911700302620">"Dacă nu doriți ca <xliff:g id="NAME">%s</xliff:g> să utilizeze această funcție, atingeți pentru a deschide setările și dezactivați-o."</string> - <string name="pip_play" msgid="3496151081459417097">"Redați"</string> - <string name="pip_pause" msgid="690688849510295232">"Întrerupeți"</string> + <string name="pip_play" msgid="3496151081459417097">"Redă"</string> + <string name="pip_pause" msgid="690688849510295232">"Întrerupe"</string> <string name="pip_skip_to_next" msgid="8403429188794867653">"Treceți la următorul"</string> <string name="pip_skip_to_prev" msgid="7172158111196394092">"Treceți la cel anterior"</string> <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionați"</string> @@ -48,17 +48,17 @@ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Partea de jos pe ecran complet"</string> <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Folosirea modului cu o mână"</string> <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Pentru a ieși, glisați în sus din partea de jos a ecranului sau atingeți oriunde deasupra ferestrei aplicației"</string> - <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Activați modul cu o mână"</string> + <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Activează modul cu o mână"</string> <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Părăsiți modul cu o mână"</string> <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Setări pentru baloanele <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Suplimentar"</string> <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Adaugă înapoi în stivă"</string> <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de la <xliff:g id="APP_NAME">%2$s</xliff:g>"</string> <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de la <xliff:g id="APP_NAME">%2$s</xliff:g> și încă <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string> - <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mutați în stânga sus"</string> - <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mutați în dreapta sus"</string> - <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mutați în stânga jos"</string> - <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mutați în dreapta jos"</string> + <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mută în stânga sus"</string> + <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mută în dreapta sus"</string> + <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mută în stânga jos"</string> + <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mută în dreapta jos"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Setări <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_dismiss_text" msgid="8816558050659478158">"Închide balonul"</string> <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nu afișați conversația în balon"</string> @@ -70,7 +70,7 @@ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nu există baloane recente"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Baloanele recente și baloanele respinse vor apărea aici"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string> - <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</string> + <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionează"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string> <string name="restart_button_description" msgid="6712141648865547958">"Atingeți ca să reporniți aplicația pentru o vizualizare mai bună."</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Aveți probleme cu camera foto?\nAtingeți pentru a reîncadra"</string> @@ -80,7 +80,7 @@ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Trage în altă aplicație pentru a folosi ecranul împărțit"</string> <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Atinge de două ori lângă o aplicație pentru a o repoziționa"</string> <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string> - <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Extindeți pentru mai multe informații"</string> + <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Extinde pentru mai multe informații"</string> <string name="maximize_button_text" msgid="1650859196290301963">"Maximizați"</string> <string name="minimize_button_text" msgid="271592547935841753">"Minimizează"</string> <string name="close_button_text" msgid="2913281996024033299">"Închide"</string> diff --git a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml index ad872f8be9ba..b5245ffbf0bc 100644 --- a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml +++ b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml @@ -21,14 +21,14 @@ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program fără titlu)"</string> <string name="pip_close" msgid="2955969519031223530">"Închide"</string> <string name="pip_fullscreen" msgid="7278047353591302554">"Ecran complet"</string> - <string name="pip_move" msgid="158770205886688553">"Mutați"</string> - <string name="pip_expand" msgid="1051966011679297308">"Extindeți"</string> - <string name="pip_collapse" msgid="3903295106641385962">"Restrângeți"</string> + <string name="pip_move" msgid="158770205886688553">"Mută"</string> + <string name="pip_expand" msgid="1051966011679297308">"Extinde"</string> + <string name="pip_collapse" msgid="3903295106641385962">"Restrânge"</string> <string name="pip_edu_text" msgid="3672999496647508701">" Apasă de două ori "<annotation icon="home_icon">"butonul ecran de pornire"</annotation>" pentru comenzi"</string> <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Meniu picture-in-picture."</string> - <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mutați spre stânga"</string> - <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mutați spre dreapta"</string> - <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mutați în sus"</string> - <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mutați în jos"</string> + <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mută la stânga"</string> + <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mută la dreapta"</string> + <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mută în sus"</string> + <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mută în jos"</string> <string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Gata"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 5696b8dfa8e3..0bc70857a113 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -297,4 +297,28 @@ when the pinned stack size is overridden by app via minWidth/minHeight. --> <dimen name="overridable_minimal_size_pip_resizable_task">48dp</dimen> + + <!-- The size of the drag handle / menu shown along with a floating task. --> + <dimen name="floating_task_menu_size">32dp</dimen> + + <!-- The size of menu items in the floating task menu. --> + <dimen name="floating_task_menu_item_size">24dp</dimen> + + <!-- The horizontal margin of menu items in the floating task menu. --> + <dimen name="floating_task_menu_item_padding">5dp</dimen> + + <!-- The width of visible floating view region when stashed. --> + <dimen name="floating_task_stash_offset">32dp</dimen> + + <!-- The amount of elevation for a floating task. --> + <dimen name="floating_task_elevation">8dp</dimen> + + <!-- The amount of padding around the bottom and top of the task. --> + <dimen name="floating_task_vertical_padding">8dp</dimen> + + <!-- The normal size of the dismiss target. --> + <dimen name="floating_task_dismiss_circle_size">150dp</dimen> + + <!-- The smaller size of the dismiss target (shrinks when something is in the target). --> + <dimen name="floating_dismiss_circle_small">120dp</dimen> </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java index d88cc007c7b5..1c0e6f726fbf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java @@ -18,6 +18,7 @@ package com.android.wm.shell.activityembedding; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET; +import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW; import android.animation.Animator; import android.animation.ValueAnimator; @@ -129,12 +130,20 @@ class ActivityEmbeddingAnimationRunner { @NonNull private List<ActivityEmbeddingAnimationAdapter> createAnimationAdapters( @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) { + boolean isChangeTransition = false; for (TransitionInfo.Change change : info.getChanges()) { - if (change.getMode() == TRANSIT_CHANGE + if (change.hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW)) { + // Skip the animation if the windows are behind an app starting window. + return new ArrayList<>(); + } + if (!isChangeTransition && change.getMode() == TRANSIT_CHANGE && !change.getStartAbsBounds().equals(change.getEndAbsBounds())) { - return createChangeAnimationAdapters(info, startTransaction); + isChangeTransition = true; } } + if (isChangeTransition) { + return createChangeAnimationAdapters(info, startTransaction); + } if (Transitions.isClosingType(info.getType())) { return createCloseAnimationAdapters(info); } @@ -276,7 +285,8 @@ class ActivityEmbeddingAnimationRunner { } final Animation animation; - if (!TransitionInfo.isIndependent(change, info)) { + if (change.getParent() != null + && handledChanges.contains(info.getChange(change.getParent()))) { // No-op if it will be covered by the changing parent window. animation = ActivityEmbeddingAnimationSpec.createNoopAnimation(change); } else if (Transitions.isClosingType(change.getMode())) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index b5a575499a3a..922472a26113 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -59,6 +59,8 @@ import java.util.concurrent.Executor; public class Bubble implements BubbleViewProvider { private static final String TAG = "Bubble"; + public static final String KEY_APP_BUBBLE = "key_app_bubble"; + private final String mKey; @Nullable private final String mGroupKey; @@ -164,6 +166,14 @@ public class Bubble implements BubbleViewProvider { private PendingIntent mDeleteIntent; /** + * Used only for a special bubble in the stack that has the key {@link #KEY_APP_BUBBLE}. + * There can only be one of these bubbles in the stack and this intent will be populated for + * that bubble. + */ + @Nullable + private Intent mAppIntent; + + /** * Create a bubble with limited information based on given {@link ShortcutInfo}. * Note: Currently this is only being used when the bubble is persisted to disk. */ @@ -192,6 +202,22 @@ public class Bubble implements BubbleViewProvider { mBubbleMetadataFlagListener = listener; } + public Bubble(Intent intent, + UserHandle user, + Executor mainExecutor) { + mKey = KEY_APP_BUBBLE; + mGroupKey = null; + mLocusId = null; + mFlags = 0; + mUser = user; + mShowBubbleUpdateDot = false; + mMainExecutor = mainExecutor; + mTaskId = INVALID_TASK_ID; + mAppIntent = intent; + mDesiredHeight = Integer.MAX_VALUE; + mPackageName = intent.getPackage(); + } + @VisibleForTesting(visibility = PRIVATE) public Bubble(@NonNull final BubbleEntry entry, final Bubbles.BubbleMetadataFlagListener listener, @@ -417,6 +443,9 @@ public class Bubble implements BubbleViewProvider { mShortcutInfo = info.shortcutInfo; mAppName = info.appName; + if (mTitle == null) { + mTitle = mAppName; + } mFlyoutMessage = info.flyoutMessage; mBadgeBitmap = info.badgeBitmap; @@ -520,7 +549,7 @@ public class Bubble implements BubbleViewProvider { * @return the last time this bubble was updated or accessed, whichever is most recent. */ long getLastActivity() { - return Math.max(mLastUpdated, mLastAccessed); + return isAppBubble() ? Long.MAX_VALUE : Math.max(mLastUpdated, mLastAccessed); } /** @@ -719,6 +748,15 @@ public class Bubble implements BubbleViewProvider { return mDeleteIntent; } + @Nullable + Intent getAppBubbleIntent() { + return mAppIntent; + } + + boolean isAppBubble() { + return KEY_APP_BUBBLE.equals(mKey); + } + Intent getSettingsIntent(final Context context) { final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 0dfba3464f23..93413dbe7e5f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1017,6 +1017,20 @@ public class BubbleController implements ConfigurationChangeListener { } /** + * Adds a bubble for a specific intent. These bubbles are <b>not</b> backed by a notification + * and remain until the user dismisses the bubble or bubble stack. Only one intent bubble + * is supported at a time. + * + * @param intent the intent to display in the bubble expanded view. + */ + public void addAppBubble(Intent intent) { + if (intent == null || intent.getPackage() == null) return; + Bubble b = new Bubble(intent, UserHandle.of(mCurrentUserId), mMainExecutor); + b.setShouldAutoExpand(true); + inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false); + } + + /** * Fills the overflow bubbles by loading them from disk. */ void loadOverflowBubblesFromDisk() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index 840b2856270c..06f232cb7489 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -224,15 +224,23 @@ public class BubbleExpandedView extends LinearLayout { try { options.setTaskAlwaysOnTop(true); options.setLaunchedFromBubble(true); - if (!mIsOverflow && mBubble.hasMetadataShortcutId()) { + + Intent fillInIntent = new Intent(); + // Apply flags to make behaviour match documentLaunchMode=always. + fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT); + fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); + + if (mBubble.isAppBubble()) { + PendingIntent pi = PendingIntent.getActivity(mContext, 0, + mBubble.getAppBubbleIntent(), + PendingIntent.FLAG_MUTABLE, + null); + mTaskView.startActivity(pi, fillInIntent, options, launchBounds); + } else if (!mIsOverflow && mBubble.hasMetadataShortcutId()) { options.setApplyActivityFlagsForBubbles(true); mTaskView.startShortcutActivity(mBubble.getShortcutInfo(), options, launchBounds); } else { - Intent fillInIntent = new Intent(); - // Apply flags to make behaviour match documentLaunchMode=always. - fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT); - fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); if (mBubble != null) { mBubble.setIntentActive(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java index 976fba52b9e2..e0c782d1675b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java @@ -49,6 +49,8 @@ public class DismissCircleView extends FrameLayout { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); + final Resources res = getResources(); + setBackground(res.getDrawable(R.drawable.dismiss_circle_background)); setViewSizes(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 7c3c14e2210c..80cdd1f79cb5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -24,6 +24,7 @@ import android.content.pm.PackageManager; import android.os.Handler; import android.os.SystemProperties; import android.view.IWindowManager; +import android.view.WindowManager; import com.android.internal.logging.UiEventLogger; import com.android.launcher3.icons.IconProvider; @@ -56,10 +57,14 @@ import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.annotations.ShellSplashscreenThread; import com.android.wm.shell.compatui.CompatUIController; import com.android.wm.shell.desktopmode.DesktopMode; +import com.android.wm.shell.desktopmode.DesktopModeController; +import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; import com.android.wm.shell.displayareahelper.DisplayAreaHelperController; import com.android.wm.shell.draganddrop.DragAndDropController; +import com.android.wm.shell.floating.FloatingTasks; +import com.android.wm.shell.floating.FloatingTasksController; import com.android.wm.shell.freeform.FreeformComponents; import com.android.wm.shell.fullscreen.FullscreenTaskListener; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; @@ -479,12 +484,14 @@ public abstract class WMShellBaseModule { ShellInit shellInit, ShellCommandHandler shellCommandHandler, TaskStackListenerImpl taskStackListener, + ActivityTaskManager activityTaskManager, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, @ShellMainThread ShellExecutor mainExecutor ) { return Optional.ofNullable( RecentTasksController.create(context, shellInit, shellCommandHandler, - taskStackListener, desktopModeTaskRepository, mainExecutor)); + taskStackListener, activityTaskManager, desktopModeTaskRepository, + mainExecutor)); } // @@ -572,6 +579,47 @@ public abstract class WMShellBaseModule { } // + // Floating tasks + // + + @WMSingleton + @Provides + static Optional<FloatingTasks> provideFloatingTasks( + Optional<FloatingTasksController> floatingTaskController) { + return floatingTaskController.map((controller) -> controller.asFloatingTasks()); + } + + @WMSingleton + @Provides + static Optional<FloatingTasksController> provideFloatingTasksController(Context context, + ShellInit shellInit, + ShellController shellController, + ShellCommandHandler shellCommandHandler, + Optional<BubbleController> bubbleController, + WindowManager windowManager, + ShellTaskOrganizer organizer, + TaskViewTransitions taskViewTransitions, + @ShellMainThread ShellExecutor mainExecutor, + @ShellBackgroundThread ShellExecutor bgExecutor, + SyncTransactionQueue syncQueue) { + if (FloatingTasksController.FLOATING_TASKS_ENABLED) { + return Optional.of(new FloatingTasksController(context, + shellInit, + shellController, + shellCommandHandler, + bubbleController, + windowManager, + organizer, + taskViewTransitions, + mainExecutor, + bgExecutor, + syncQueue)); + } else { + return Optional.empty(); + } + } + + // // Starting window // @@ -672,15 +720,36 @@ public abstract class WMShellBaseModule { // Desktop mode (optional feature) // + @WMSingleton + @Provides + static Optional<DesktopMode> provideDesktopMode( + Optional<DesktopModeController> desktopModeController) { + return desktopModeController.map(DesktopModeController::asDesktopMode); + } + + @BindsOptionalOf + @DynamicOverride + abstract DesktopModeController optionalDesktopModeController(); + + @WMSingleton + @Provides + static Optional<DesktopModeController> providesDesktopModeController( + @DynamicOverride Optional<DesktopModeController> desktopModeController) { + if (DesktopModeStatus.IS_SUPPORTED) { + return desktopModeController; + } + return Optional.empty(); + } + @BindsOptionalOf @DynamicOverride abstract DesktopModeTaskRepository optionalDesktopModeTaskRepository(); @WMSingleton @Provides - static Optional<DesktopModeTaskRepository> providesDesktopModeTaskRepository( + static Optional<DesktopModeTaskRepository> providesDesktopTaskRepository( @DynamicOverride Optional<DesktopModeTaskRepository> desktopModeTaskRepository) { - if (DesktopMode.IS_SUPPORTED) { + if (DesktopModeStatus.IS_SUPPORTED) { return desktopModeTaskRepository; } return Optional.empty(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 35e88e9abb3c..c11c5777e818 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -48,7 +48,6 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; -import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.draganddrop.DragAndDropController; @@ -319,6 +318,7 @@ public abstract class WMShellModule { ShellCommandHandler shellCommandHandler, ShellController shellController, DisplayController displayController, + PipAnimationController pipAnimationController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, PhonePipKeepClearAlgorithm pipKeepClearAlgorithm, @@ -338,11 +338,12 @@ public abstract class WMShellModule { @ShellMainThread ShellExecutor mainExecutor) { return Optional.ofNullable(PipController.create( context, shellInit, shellCommandHandler, shellController, - displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm, - pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController, - pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController, - windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder, - displayInsetsController, oneHandedController, mainExecutor)); + displayController, pipAnimationController, pipAppOpsListener, pipBoundsAlgorithm, + pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController, + phonePipMenuController, pipTaskOrganizer, pipTransitionState, pipTouchHandler, + pipTransitionController, windowManagerShellWrapper, taskStackListener, + pipParamsChangedForwarder, displayInsetsController, oneHandedController, + mainExecutor)); } @WMSingleton @@ -595,19 +596,18 @@ public abstract class WMShellModule { @WMSingleton @Provides - static Optional<DesktopModeController> provideDesktopModeController( - Context context, ShellInit shellInit, + @DynamicOverride + static DesktopModeController provideDesktopModeController(Context context, ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + Transitions transitions, + @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository, @ShellMainThread Handler mainHandler, - Transitions transitions + @ShellMainThread ShellExecutor mainExecutor ) { - if (DesktopMode.IS_SUPPORTED) { - return Optional.of(new DesktopModeController(context, shellInit, shellTaskOrganizer, - rootTaskDisplayAreaOrganizer, mainHandler, transitions)); - } else { - return Optional.empty(); - } + return new DesktopModeController(context, shellInit, shellTaskOrganizer, + rootTaskDisplayAreaOrganizer, transitions, desktopModeTaskRepository, mainHandler, + mainExecutor); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java index 8993d549964c..ff3be38d09e1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java @@ -16,43 +16,16 @@ package com.android.wm.shell.desktopmode; -import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; - -import android.content.Context; -import android.os.SystemProperties; -import android.os.UserHandle; -import android.provider.Settings; - -import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.common.annotations.ExternalThread; /** - * Constants for desktop mode feature + * Interface to interact with desktop mode feature in shell. */ -public class DesktopMode { - - /** - * Flag to indicate whether desktop mode is available on the device - */ - public static final boolean IS_SUPPORTED = SystemProperties.getBoolean( - "persist.wm.debug.desktop_mode", false); +@ExternalThread +public interface DesktopMode { - /** - * Check if desktop mode is active - * - * @return {@code true} if active - */ - public static boolean isActive(Context context) { - if (!IS_SUPPORTED) { - return false; - } - try { - int result = Settings.System.getIntForUser(context.getContentResolver(), - Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT); - ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result); - return result != 0; - } catch (Exception e) { - ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e); - return false; - } + /** Returns a binder that can be passed to an external process to manipulate DesktopMode. */ + default IDesktopMode createExternalInterface() { + return null; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java index 6e44d58cffae..9474cfe916f0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java @@ -20,8 +20,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.TRANSIT_CHANGE; +import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; +import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration; import android.content.Context; import android.database.ContentObserver; @@ -29,51 +31,83 @@ import android.net.Uri; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; +import android.util.ArraySet; import android.window.DisplayAreaInfo; import android.window.WindowContainerTransaction; +import androidx.annotation.BinderThread; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.RemoteCallable; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; +import java.util.ArrayList; +import java.util.Comparator; + /** * Handles windowing changes when desktop mode system setting changes */ -public class DesktopModeController { +public class DesktopModeController implements RemoteCallable<DesktopModeController> { private final Context mContext; private final ShellTaskOrganizer mShellTaskOrganizer; private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; - private final SettingsObserver mSettingsObserver; private final Transitions mTransitions; + private final DesktopModeTaskRepository mDesktopModeTaskRepository; + private final ShellExecutor mMainExecutor; + private final DesktopMode mDesktopModeImpl = new DesktopModeImpl(); + private final SettingsObserver mSettingsObserver; public DesktopModeController(Context context, ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + Transitions transitions, + DesktopModeTaskRepository desktopModeTaskRepository, @ShellMainThread Handler mainHandler, - Transitions transitions) { + @ShellMainThread ShellExecutor mainExecutor) { mContext = context; mShellTaskOrganizer = shellTaskOrganizer; mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; - mSettingsObserver = new SettingsObserver(mContext, mainHandler); mTransitions = transitions; + mDesktopModeTaskRepository = desktopModeTaskRepository; + mMainExecutor = mainExecutor; + mSettingsObserver = new SettingsObserver(mContext, mainHandler); shellInit.addInitCallback(this::onInit, this); } private void onInit() { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopModeController"); mSettingsObserver.observe(); - if (DesktopMode.isActive(mContext)) { + if (DesktopModeStatus.isActive(mContext)) { updateDesktopModeActive(true); } } + @Override + public Context getContext() { + return mContext; + } + + @Override + public ShellExecutor getRemoteCallExecutor() { + return mMainExecutor; + } + + /** + * Get connection interface between sysui and shell + */ + public DesktopMode asDesktopMode() { + return mDesktopModeImpl; + } + @VisibleForTesting void updateDesktopModeActive(boolean active) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeActive: active=%s", active); @@ -121,6 +155,28 @@ public class DesktopModeController { } /** + * Show apps on desktop + */ + public void showDesktopApps() { + ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks(); + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size()); + ArrayList<RunningTaskInfo> taskInfos = new ArrayList<>(); + for (Integer taskId : activeTasks) { + RunningTaskInfo taskInfo = mShellTaskOrganizer.getRunningTaskInfo(taskId); + if (taskInfo != null) { + taskInfos.add(taskInfo); + } + } + // Order by lastActiveTime, descending + taskInfos.sort(Comparator.comparingLong(task -> -task.lastActiveTime)); + WindowContainerTransaction wct = new WindowContainerTransaction(); + for (RunningTaskInfo task : taskInfos) { + wct.reorder(task.token, true); + } + mShellTaskOrganizer.applyTransaction(wct); + } + + /** * A {@link ContentObserver} for listening to changes to {@link Settings.System#DESKTOP_MODE} */ private final class SettingsObserver extends ContentObserver { @@ -150,8 +206,51 @@ public class DesktopModeController { } private void desktopModeSettingChanged() { - boolean enabled = DesktopMode.isActive(mContext); + boolean enabled = DesktopModeStatus.isActive(mContext); updateDesktopModeActive(enabled); } } + + /** + * The interface for calls from outside the shell, within the host process. + */ + @ExternalThread + private final class DesktopModeImpl implements DesktopMode { + + private IDesktopModeImpl mIDesktopMode; + + @Override + public IDesktopMode createExternalInterface() { + if (mIDesktopMode != null) { + mIDesktopMode.invalidate(); + } + mIDesktopMode = new IDesktopModeImpl(DesktopModeController.this); + return mIDesktopMode; + } + } + + /** + * The interface for calls from outside the host process. + */ + @BinderThread + private static class IDesktopModeImpl extends IDesktopMode.Stub { + + private DesktopModeController mController; + + IDesktopModeImpl(DesktopModeController controller) { + mController = controller; + } + + /** + * Invalidates this instance, preventing future calls from updating the controller. + */ + void invalidate() { + mController = null; + } + + public void showDesktopApps() { + executeRemoteCallWithTaskPermission(mController, "showDesktopApps", + DesktopModeController::showDesktopApps); + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java new file mode 100644 index 000000000000..195ff502e7dc --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022 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.wm.shell.desktopmode; + +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; + +import android.content.Context; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.provider.Settings; + +import com.android.internal.protolog.common.ProtoLog; + +/** + * Constants for desktop mode feature + */ +public class DesktopModeStatus { + + /** + * Flag to indicate whether desktop mode is available on the device + */ + public static final boolean IS_SUPPORTED = SystemProperties.getBoolean( + "persist.wm.debug.desktop_mode", false); + + /** + * Check if desktop mode is active + * + * @return {@code true} if active + */ + public static boolean isActive(Context context) { + if (!IS_SUPPORTED) { + return false; + } + try { + int result = Settings.System.getIntForUser(context.getContentResolver(), + Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT); + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result); + return result != 0; + } catch (Exception e) { + ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e); + return false; + } + } +} diff --git a/core/java/android/app/cloudsearch/SearchResult.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl index daebfbf3307f..5042bd6f2d65 100644 --- a/core/java/android/app/cloudsearch/SearchResult.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2022, The Android Open Source Project +/* + * Copyright (C) 2022 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 + * 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, @@ -14,6 +14,13 @@ * limitations under the License. */ -package android.app.cloudsearch; +package com.android.wm.shell.desktopmode; + +/** + * Interface that is exposed to remote callers to manipulate desktop mode features. + */ +interface IDesktopMode { -parcelable SearchResult;
\ No newline at end of file + /** Show apps on the desktop */ + void showDesktopApps(); +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingDismissController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingDismissController.java new file mode 100644 index 000000000000..83a1734dc71a --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingDismissController.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2022 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.wm.shell.floating; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.view.MotionEvent; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.dynamicanimation.animation.DynamicAnimation; + +import com.android.wm.shell.R; +import com.android.wm.shell.bubbles.DismissView; +import com.android.wm.shell.common.magnetictarget.MagnetizedObject; +import com.android.wm.shell.floating.views.FloatingTaskLayer; +import com.android.wm.shell.floating.views.FloatingTaskView; + +import java.util.Objects; + +/** + * Controls a floating dismiss circle that has a 'magnetic' field around it, causing views moved + * close to the target to be stuck to it unless moved out again. + */ +public class FloatingDismissController { + + /** Velocity required to dismiss the view without dragging it into the dismiss target. */ + private static final float FLING_TO_DISMISS_MIN_VELOCITY = 4000f; + /** + * Max velocity that the view can be moving through the target with to stick (i.e. if it's + * more than this velocity, it will pass through the target. + */ + private static final float STICK_TO_TARGET_MAX_X_VELOCITY = 2000f; + /** + * Percentage of the target width to use to determine if an object flung towards the target + * should dismiss (e.g. if target is 100px and this is set ot 2f, anything flung within a + * 200px-wide area around the target will be considered 'near' enough get dismissed). + */ + private static final float FLING_TO_TARGET_WIDTH_PERCENT = 2f; + /** Minimum alpha to apply to the view being dismissed when it is in the target. */ + private static final float DISMISS_VIEW_MIN_ALPHA = 0.6f; + /** Amount to scale down the view being dismissed when it is in the target. */ + private static final float DISMISS_VIEW_SCALE_DOWN_PERCENT = 0.15f; + + private Context mContext; + private FloatingTasksController mController; + private FloatingTaskLayer mParent; + + private DismissView mDismissView; + private ValueAnimator mDismissAnimator; + private View mViewBeingDismissed; + private float mDismissSizePercent; + private float mDismissSize; + + /** + * The currently magnetized object, which is being dragged and will be attracted to the magnetic + * dismiss target. + */ + private MagnetizedObject<View> mMagnetizedObject; + /** + * The MagneticTarget instance for our circular dismiss view. This is added to the + * MagnetizedObject instances for the view being dragged. + */ + private MagnetizedObject.MagneticTarget mMagneticTarget; + /** Magnet listener that handles animating and dismissing the view. */ + private MagnetizedObject.MagnetListener mFloatingViewMagnetListener; + + public FloatingDismissController(Context context, FloatingTasksController controller, + FloatingTaskLayer parent) { + mContext = context; + mController = controller; + mParent = parent; + updateSizes(); + createAndAddDismissView(); + + mDismissAnimator = ValueAnimator.ofFloat(1f, 0f); + mDismissAnimator.addUpdateListener(animation -> { + final float value = (float) animation.getAnimatedValue(); + if (mDismissView != null) { + mDismissView.setPivotX((mDismissView.getRight() - mDismissView.getLeft()) / 2f); + mDismissView.setPivotY((mDismissView.getBottom() - mDismissView.getTop()) / 2f); + final float scaleValue = Math.max(value, mDismissSizePercent); + mDismissView.getCircle().setScaleX(scaleValue); + mDismissView.getCircle().setScaleY(scaleValue); + } + if (mViewBeingDismissed != null) { + // TODO: alpha doesn't actually apply to taskView currently. + mViewBeingDismissed.setAlpha(Math.max(value, DISMISS_VIEW_MIN_ALPHA)); + mViewBeingDismissed.setScaleX(Math.max(value, DISMISS_VIEW_SCALE_DOWN_PERCENT)); + mViewBeingDismissed.setScaleY(Math.max(value, DISMISS_VIEW_SCALE_DOWN_PERCENT)); + } + }); + + mFloatingViewMagnetListener = new MagnetizedObject.MagnetListener() { + @Override + public void onStuckToTarget( + @NonNull MagnetizedObject.MagneticTarget target) { + animateDismissing(/* dismissing= */ true); + } + + @Override + public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target, + float velX, float velY, boolean wasFlungOut) { + animateDismissing(/* dismissing= */ false); + mParent.onUnstuckFromTarget((FloatingTaskView) mViewBeingDismissed, velX, velY, + wasFlungOut); + } + + @Override + public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) { + doDismiss(); + } + }; + } + + /** Updates all the sizes used and applies them to the {@link DismissView}. */ + public void updateSizes() { + Resources res = mContext.getResources(); + mDismissSize = res.getDimensionPixelSize( + R.dimen.floating_task_dismiss_circle_size); + final float minDismissSize = res.getDimensionPixelSize( + R.dimen.floating_dismiss_circle_small); + mDismissSizePercent = minDismissSize / mDismissSize; + + if (mDismissView != null) { + mDismissView.updateResources(); + } + } + + /** Prepares the view being dragged to be magnetic. */ + public void setUpMagneticObject(View viewBeingDragged) { + mViewBeingDismissed = viewBeingDragged; + mMagnetizedObject = getMagnetizedView(viewBeingDragged); + mMagnetizedObject.clearAllTargets(); + mMagnetizedObject.addTarget(mMagneticTarget); + mMagnetizedObject.setMagnetListener(mFloatingViewMagnetListener); + } + + /** Shows or hides the dismiss target. */ + public void showDismiss(boolean show) { + if (show) { + mDismissView.show(); + } else { + mDismissView.hide(); + } + } + + /** Passes the MotionEvent to the magnetized object and returns true if it was consumed. */ + public boolean passEventToMagnetizedObject(MotionEvent event) { + return mMagnetizedObject != null && mMagnetizedObject.maybeConsumeMotionEvent(event); + } + + private void createAndAddDismissView() { + if (mDismissView != null) { + mParent.removeView(mDismissView); + } + mDismissView = new DismissView(mContext); + mDismissView.setTargetSizeResId(R.dimen.floating_task_dismiss_circle_size); + mDismissView.updateResources(); + mParent.addView(mDismissView); + + final float dismissRadius = mDismissSize; + // Save the MagneticTarget instance for the newly set up view - we'll add this to the + // MagnetizedObjects when the dismiss view gets shown. + mMagneticTarget = new MagnetizedObject.MagneticTarget( + mDismissView.getCircle(), (int) dismissRadius); + } + + private MagnetizedObject<View> getMagnetizedView(View v) { + if (mMagnetizedObject != null + && Objects.equals(mMagnetizedObject.getUnderlyingObject(), v)) { + // Same view being dragged, we can reuse the magnetic object. + return mMagnetizedObject; + } + MagnetizedObject<View> magnetizedView = new MagnetizedObject<View>( + mContext, + v, + DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y + ) { + @Override + public float getWidth(@NonNull View underlyingObject) { + return underlyingObject.getWidth(); + } + + @Override + public float getHeight(@NonNull View underlyingObject) { + return underlyingObject.getHeight(); + } + + @Override + public void getLocationOnScreen(@NonNull View underlyingObject, + @NonNull int[] loc) { + loc[0] = (int) underlyingObject.getTranslationX(); + loc[1] = (int) underlyingObject.getTranslationY(); + } + }; + magnetizedView.setHapticsEnabled(true); + magnetizedView.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY); + magnetizedView.setStickToTargetMaxXVelocity(STICK_TO_TARGET_MAX_X_VELOCITY); + magnetizedView.setFlingToTargetWidthPercent(FLING_TO_TARGET_WIDTH_PERCENT); + return magnetizedView; + } + + /** Animates the dismiss treatment on the view being dismissed. */ + private void animateDismissing(boolean shouldDismiss) { + if (mViewBeingDismissed == null) { + return; + } + if (shouldDismiss) { + mDismissAnimator.removeAllListeners(); + mDismissAnimator.start(); + } else { + mDismissAnimator.removeAllListeners(); + mDismissAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + resetDismissAnimator(); + } + }); + mDismissAnimator.reverse(); + } + } + + /** Actually dismisses the view. */ + private void doDismiss() { + mDismissView.hide(); + mController.removeTask(); + resetDismissAnimator(); + mViewBeingDismissed = null; + } + + private void resetDismissAnimator() { + mDismissAnimator.removeAllListeners(); + mDismissAnimator.cancel(); + if (mDismissView != null) { + mDismissView.cancelAnimators(); + mDismissView.getCircle().setScaleX(1f); + mDismissView.getCircle().setScaleY(1f); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java new file mode 100644 index 000000000000..935666026bf4 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 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.wm.shell.floating; + +import android.content.Intent; + +import com.android.wm.shell.common.annotations.ExternalThread; + +/** + * Interface to interact with floating tasks. + */ +@ExternalThread +public interface FloatingTasks { + + /** + * Shows, stashes, or un-stashes the floating task depending on state: + * - If there is no floating task for this intent, it shows the task for the provided intent. + * - If there is a floating task for this intent, but it's stashed, this un-stashes it. + * - If there is a floating task for this intent, and it's not stashed, this stashes it. + */ + void showOrSetStashed(Intent intent); + + /** Returns a binder that can be passed to an external process to manipulate FloatingTasks. */ + default IFloatingTasks createExternalInterface() { + return null; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java new file mode 100644 index 000000000000..67552991869b --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java @@ -0,0 +1,455 @@ +/* + * Copyright (C) 2022 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.wm.shell.floating; + +import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + +import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FLOATING_APPS; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ShortcutInfo; +import android.content.res.Configuration; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.os.SystemProperties; +import android.view.ViewGroup; +import android.view.WindowManager; + +import androidx.annotation.BinderThread; +import androidx.annotation.VisibleForTesting; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.TaskViewTransitions; +import com.android.wm.shell.bubbles.BubbleController; +import com.android.wm.shell.common.RemoteCallable; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.common.annotations.ExternalThread; +import com.android.wm.shell.common.annotations.ShellBackgroundThread; +import com.android.wm.shell.common.annotations.ShellMainThread; +import com.android.wm.shell.floating.views.FloatingTaskLayer; +import com.android.wm.shell.floating.views.FloatingTaskView; +import com.android.wm.shell.sysui.ConfigurationChangeListener; +import com.android.wm.shell.sysui.ShellCommandHandler; +import com.android.wm.shell.sysui.ShellController; +import com.android.wm.shell.sysui.ShellInit; + +import java.io.PrintWriter; +import java.util.Objects; +import java.util.Optional; + +/** + * Entry point for creating and managing floating tasks. + * + * A single window layer is added and the task(s) are displayed using a {@link FloatingTaskView} + * within that window. + * + * Currently optimized for a single task. Multiple tasks are not supported. + */ +public class FloatingTasksController implements RemoteCallable<FloatingTasksController>, + ConfigurationChangeListener { + + private static final String TAG = FloatingTasksController.class.getSimpleName(); + + public static final boolean FLOATING_TASKS_ENABLED = + SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false); + public static final boolean SHOW_FLOATING_TASKS_AS_BUBBLES = + SystemProperties.getBoolean("persist.wm.debug.floating_tasks_as_bubbles", false); + + @VisibleForTesting + static final int SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET = 600; + + // Only used for testing + private Configuration mConfig; + private boolean mFloatingTasksEnabledForTests; + + private FloatingTaskImpl mImpl = new FloatingTaskImpl(); + private Context mContext; + private ShellController mShellController; + private ShellCommandHandler mShellCommandHandler; + private @Nullable BubbleController mBubbleController; + private WindowManager mWindowManager; + private ShellTaskOrganizer mTaskOrganizer; + private TaskViewTransitions mTaskViewTransitions; + private @ShellMainThread ShellExecutor mMainExecutor; + // TODO: mBackgroundThread is not used but we'll probs need it eventually? + private @ShellBackgroundThread ShellExecutor mBackgroundThread; + private SyncTransactionQueue mSyncQueue; + + private boolean mIsFloatingLayerAdded; + private FloatingTaskLayer mFloatingTaskLayer; + private final Point mLastPosition = new Point(-1, -1); + + private Task mTask; + + // Simple class to hold onto info for intent or shortcut based tasks. + public static class Task { + public int taskId = INVALID_TASK_ID; + @Nullable + public Intent intent; + @Nullable + public ShortcutInfo info; + @Nullable + public FloatingTaskView floatingView; + } + + public FloatingTasksController(Context context, + ShellInit shellInit, + ShellController shellController, + ShellCommandHandler shellCommandHandler, + Optional<BubbleController> bubbleController, + WindowManager windowManager, + ShellTaskOrganizer organizer, + TaskViewTransitions transitions, + @ShellMainThread ShellExecutor mainExecutor, + @ShellBackgroundThread ShellExecutor bgExceutor, + SyncTransactionQueue syncTransactionQueue) { + mContext = context; + mShellController = shellController; + mShellCommandHandler = shellCommandHandler; + mBubbleController = bubbleController.get(); + mWindowManager = windowManager; + mTaskOrganizer = organizer; + mTaskViewTransitions = transitions; + mMainExecutor = mainExecutor; + mBackgroundThread = bgExceutor; + mSyncQueue = syncTransactionQueue; + if (isFloatingTasksEnabled()) { + shellInit.addInitCallback(this::onInit, this); + } + mShellCommandHandler.addDumpCallback(this::dump, this); + } + + protected void onInit() { + mShellController.addConfigurationChangeListener(this); + } + + /** Only used for testing. */ + @VisibleForTesting + void setConfig(Configuration config) { + mConfig = config; + } + + /** Only used for testing. */ + @VisibleForTesting + void setFloatingTasksEnabled(boolean enabled) { + mFloatingTasksEnabledForTests = enabled; + } + + /** Whether the floating layer is available. */ + boolean isFloatingLayerAvailable() { + Configuration config = mConfig == null + ? mContext.getResources().getConfiguration() + : mConfig; + return config.smallestScreenWidthDp >= SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET; + } + + /** Whether floating tasks are enabled. */ + boolean isFloatingTasksEnabled() { + return FLOATING_TASKS_ENABLED || mFloatingTasksEnabledForTests; + } + + @Override + public void onThemeChanged() { + if (mIsFloatingLayerAdded) { + mFloatingTaskLayer.updateSizes(); + } + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + // TODO: probably other stuff here to do (e.g. handle rotation) + if (mIsFloatingLayerAdded) { + mFloatingTaskLayer.updateSizes(); + } + } + + /** Returns false if the task shouldn't be shown. */ + private boolean canShowTask(Intent intent) { + ProtoLog.d(WM_SHELL_FLOATING_APPS, "canShowTask -- %s", intent); + if (!isFloatingTasksEnabled() || !isFloatingLayerAvailable()) return false; + if (intent == null) { + ProtoLog.e(WM_SHELL_FLOATING_APPS, "canShowTask given null intent, doing nothing"); + return false; + } + return true; + } + + /** Returns true if the task was or should be shown as a bubble. */ + private boolean maybeShowTaskAsBubble(Intent intent) { + if (SHOW_FLOATING_TASKS_AS_BUBBLES && mBubbleController != null) { + removeFloatingLayer(); + if (intent.getPackage() != null) { + mBubbleController.addAppBubble(intent); + ProtoLog.d(WM_SHELL_FLOATING_APPS, "showing floating task as bubble: %s", intent); + } else { + ProtoLog.d(WM_SHELL_FLOATING_APPS, + "failed to show floating task as bubble: %s; unknown package", intent); + } + return true; + } + return false; + } + + /** + * Shows, stashes, or un-stashes the floating task depending on state: + * - If there is no floating task for this intent, it shows this the provided task. + * - If there is a floating task for this intent, but it's stashed, this un-stashes it. + * - If there is a floating task for this intent, and it's not stashed, this stashes it. + */ + public void showOrSetStashed(Intent intent) { + if (!canShowTask(intent)) return; + if (maybeShowTaskAsBubble(intent)) return; + + addFloatingLayer(); + + if (isTaskAttached(mTask) && intent.filterEquals(mTask.intent)) { + // The task is already added, toggle the stash state. + mFloatingTaskLayer.setStashed(mTask, !mTask.floatingView.isStashed()); + return; + } + + // If we're here it's either a new or different task + showNewTask(intent); + } + + /** + * Shows a floating task with the provided intent. + * If the same task is present it will un-stash it or do nothing if it is already un-stashed. + * Removes any other floating tasks that might exist. + */ + public void showTask(Intent intent) { + if (!canShowTask(intent)) return; + if (maybeShowTaskAsBubble(intent)) return; + + addFloatingLayer(); + + if (isTaskAttached(mTask) && intent.filterEquals(mTask.intent)) { + // The task is already added, show it if it's stashed. + if (mTask.floatingView.isStashed()) { + mFloatingTaskLayer.setStashed(mTask, false); + } + return; + } + showNewTask(intent); + } + + private void showNewTask(Intent intent) { + if (mTask != null && !intent.filterEquals(mTask.intent)) { + mFloatingTaskLayer.removeAllTaskViews(); + mTask.floatingView.cleanUpTaskView(); + mTask = null; + } + + FloatingTaskView ftv = new FloatingTaskView(mContext, this); + ftv.createTaskView(mContext, mTaskOrganizer, mTaskViewTransitions, mSyncQueue); + + mTask = new Task(); + mTask.floatingView = ftv; + mTask.intent = intent; + + // Add & start the task. + mFloatingTaskLayer.addTask(mTask); + ProtoLog.d(WM_SHELL_FLOATING_APPS, "showNewTask, startingIntent: %s", intent); + mTask.floatingView.startTask(mMainExecutor, mTask); + } + + /** + * Removes the task and cleans up the view. + */ + public void removeTask() { + if (mTask != null) { + ProtoLog.d(WM_SHELL_FLOATING_APPS, "Removing task with id=%d", mTask.taskId); + + if (mTask.floatingView != null) { + // TODO: animate it + mFloatingTaskLayer.removeView(mTask.floatingView); + mTask.floatingView.cleanUpTaskView(); + } + removeFloatingLayer(); + } + } + + /** + * Whether there is a floating task and if it is stashed. + */ + public boolean isStashed() { + return isTaskAttached(mTask) && mTask.floatingView.isStashed(); + } + + /** + * If a floating task exists, this sets whether it is stashed and animates if needed. + */ + public void setStashed(boolean shouldStash) { + if (mTask != null && mTask.floatingView != null && mIsFloatingLayerAdded) { + mFloatingTaskLayer.setStashed(mTask, shouldStash); + } + } + + /** + * Saves the last position the floating task was in so that it can be put there again. + */ + public void setLastPosition(int x, int y) { + mLastPosition.set(x, y); + } + + /** + * Returns the last position the floating task was in. + */ + public Point getLastPosition() { + return mLastPosition; + } + + /** + * Whether the provided task has a view that's attached to the floating layer. + */ + private boolean isTaskAttached(Task t) { + return t != null && t.floatingView != null + && mIsFloatingLayerAdded + && mFloatingTaskLayer.getTaskViewCount() > 0 + && Objects.equals(mFloatingTaskLayer.getFirstTaskView(), t.floatingView); + } + + // TODO: when this is added, if there are bubbles, they get hidden? Is only one layer of this + // type allowed? Bubbles & floating tasks should probably be in the same layer to reduce + // # of windows. + private void addFloatingLayer() { + if (mIsFloatingLayerAdded) { + return; + } + + mFloatingTaskLayer = new FloatingTaskLayer(mContext, this, mWindowManager); + + WindowManager.LayoutParams params = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, + PixelFormat.TRANSLUCENT + ); + params.setTrustedOverlay(); + params.setFitInsetsTypes(0); + params.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; + params.setTitle("FloatingTaskLayer"); + params.packageName = mContext.getPackageName(); + params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; + + try { + mIsFloatingLayerAdded = true; + mWindowManager.addView(mFloatingTaskLayer, params); + } catch (IllegalStateException e) { + // This means the floating layer has already been added which shouldn't happen. + e.printStackTrace(); + } + } + + private void removeFloatingLayer() { + if (!mIsFloatingLayerAdded) { + return; + } + try { + mIsFloatingLayerAdded = false; + if (mFloatingTaskLayer != null) { + mWindowManager.removeView(mFloatingTaskLayer); + } + } catch (IllegalArgumentException e) { + // This means the floating layer has already been removed which shouldn't happen. + e.printStackTrace(); + } + } + + /** + * Description of current floating task state. + */ + private void dump(PrintWriter pw, String prefix) { + pw.println("FloatingTaskController state:"); + pw.print(" isFloatingLayerAvailable= "); pw.println(isFloatingLayerAvailable()); + pw.print(" isFloatingTasksEnabled= "); pw.println(isFloatingTasksEnabled()); + pw.print(" mIsFloatingLayerAdded= "); pw.println(mIsFloatingLayerAdded); + pw.print(" mLastPosition= "); pw.println(mLastPosition); + pw.println(); + } + + /** Returns the {@link FloatingTasks} implementation. */ + public FloatingTasks asFloatingTasks() { + return mImpl; + } + + @Override + public Context getContext() { + return mContext; + } + + @Override + public ShellExecutor getRemoteCallExecutor() { + return mMainExecutor; + } + + /** + * The interface for calls from outside the shell, within the host process. + */ + @ExternalThread + private class FloatingTaskImpl implements FloatingTasks { + private IFloatingTasksImpl mIFloatingTasks; + + @Override + public void showOrSetStashed(Intent intent) { + mMainExecutor.execute(() -> FloatingTasksController.this.showOrSetStashed(intent)); + } + + @Override + public IFloatingTasks createExternalInterface() { + if (mIFloatingTasks != null) { + mIFloatingTasks.invalidate(); + } + mIFloatingTasks = new IFloatingTasksImpl(FloatingTasksController.this); + return mIFloatingTasks; + } + } + + /** + * The interface for calls from outside the host process. + */ + @BinderThread + private static class IFloatingTasksImpl extends IFloatingTasks.Stub { + private FloatingTasksController mController; + + IFloatingTasksImpl(FloatingTasksController controller) { + mController = controller; + } + + /** + * Invalidates this instance, preventing future calls from updating the controller. + */ + void invalidate() { + mController = null; + } + + public void showTask(Intent intent) { + executeRemoteCallWithTaskPermission(mController, "showTask", + (controller) -> controller.showTask(intent)); + } + } +} diff --git a/core/java/android/service/cloudsearch/ICloudSearchService.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl index 104bf99f1537..f79ca1039865 100644 --- a/core/java/android/service/cloudsearch/ICloudSearchService.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl @@ -14,15 +14,15 @@ * limitations under the License. */ -package android.service.cloudsearch; +package com.android.wm.shell.floating; -import android.app.cloudsearch.SearchRequest; +import android.content.Intent; /** - * Interface from the system to CloudSearch service. - * - * @hide + * Interface that is exposed to remote callers to manipulate floating task features. */ -oneway interface ICloudSearchService { - void onSearch(in SearchRequest request); +interface IFloatingTasks { + + void showTask(in Intent intent) = 1; + } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingMenuView.java new file mode 100644 index 000000000000..c922109751ba --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingMenuView.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2022 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.wm.shell.floating.views; + +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import com.android.wm.shell.R; + +/** + * Displays the menu items for a floating task view (e.g. close). + */ +public class FloatingMenuView extends LinearLayout { + + private int mItemSize; + private int mItemMargin; + + public FloatingMenuView(Context context) { + super(context); + setOrientation(LinearLayout.HORIZONTAL); + setGravity(Gravity.CENTER); + + mItemSize = context.getResources().getDimensionPixelSize( + R.dimen.floating_task_menu_item_size); + mItemMargin = context.getResources().getDimensionPixelSize( + R.dimen.floating_task_menu_item_padding); + } + + /** Adds a clickable item to the menu bar. Items are ordered as added. */ + public void addMenuItem(@Nullable Drawable drawable, View.OnClickListener listener) { + ImageView itemView = new ImageView(getContext()); + itemView.setScaleType(ImageView.ScaleType.CENTER); + if (drawable != null) { + itemView.setImageDrawable(drawable); + } + LinearLayout.LayoutParams lp = new LayoutParams(mItemSize, + ViewGroup.LayoutParams.MATCH_PARENT); + lp.setMarginStart(mItemMargin); + lp.setMarginEnd(mItemMargin); + addView(itemView, lp); + + itemView.setOnClickListener(listener); + } + + /** + * The menu extends past the top of the TaskView because of the rounded corners. This means + * to center content in the menu we must subtract the radius (i.e. the amount of space covered + * by TaskView). + */ + public void setCornerRadius(float radius) { + setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), (int) radius); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskLayer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskLayer.java new file mode 100644 index 000000000000..16dab2415bf2 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskLayer.java @@ -0,0 +1,687 @@ +/* + * Copyright (C) 2022 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.wm.shell.floating.views; + +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FLOATING_APPS; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.Color; +import android.graphics.Insets; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.Region; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewPropertyAnimator; +import android.view.ViewTreeObserver; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowMetrics; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.dynamicanimation.animation.DynamicAnimation; +import androidx.dynamicanimation.animation.FlingAnimation; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.R; +import com.android.wm.shell.floating.FloatingDismissController; +import com.android.wm.shell.floating.FloatingTasksController; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * This is the layout that {@link FloatingTaskView}s are contained in. It handles input and + * movement of the task views. + */ +public class FloatingTaskLayer extends FrameLayout + implements ViewTreeObserver.OnComputeInternalInsetsListener { + + private static final String TAG = FloatingTaskLayer.class.getSimpleName(); + + /** How big to make the task view based on screen width of the largest size. */ + private static final float START_SIZE_WIDTH_PERCENT = 0.33f; + /** Min fling velocity required to move the view from one side of the screen to the other. */ + private static final float ESCAPE_VELOCITY = 750f; + /** Amount of friction to apply to fling animations. */ + private static final float FLING_FRICTION = 1.9f; + + private final FloatingTasksController mController; + private final FloatingDismissController mDismissController; + private final WindowManager mWindowManager; + private final TouchHandlerImpl mTouchHandler; + + private final Region mTouchableRegion = new Region(); + private final Rect mPositionRect = new Rect(); + private final Point mDefaultStartPosition = new Point(); + private final Point mTaskViewSize = new Point(); + private WindowInsets mWindowInsets; + private int mVerticalPadding; + private int mOverhangWhenStashed; + + private final List<Rect> mSystemGestureExclusionRects = Collections.singletonList(new Rect()); + private ViewTreeObserver.OnDrawListener mSystemGestureExclusionListener = + this::updateSystemGestureExclusion; + + /** Interface allowing something to handle the touch events going to a task. */ + interface FloatingTaskTouchHandler { + void onDown(@NonNull FloatingTaskView v, @NonNull MotionEvent ev, + float viewInitialX, float viewInitialY); + + void onMove(@NonNull FloatingTaskView v, @NonNull MotionEvent ev, + float dx, float dy); + + void onUp(@NonNull FloatingTaskView v, @NonNull MotionEvent ev, + float dx, float dy, float velX, float velY); + + void onClick(@NonNull FloatingTaskView v); + } + + public FloatingTaskLayer(Context context, + FloatingTasksController controller, + WindowManager windowManager) { + super(context); + // TODO: Why is this necessary? Without it FloatingTaskView does not render correctly. + setBackgroundColor(Color.argb(0, 0, 0, 0)); + + mController = controller; + mWindowManager = windowManager; + updateSizes(); + + // TODO: Might make sense to put dismiss controller in the touch handler since that's the + // main user of dismiss controller. + mDismissController = new FloatingDismissController(context, mController, this); + mTouchHandler = new TouchHandlerImpl(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + getViewTreeObserver().addOnComputeInternalInsetsListener(this); + getViewTreeObserver().addOnDrawListener(mSystemGestureExclusionListener); + setOnApplyWindowInsetsListener((view, windowInsets) -> { + if (!windowInsets.equals(mWindowInsets)) { + mWindowInsets = windowInsets; + updateSizes(); + } + return windowInsets; + }); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + getViewTreeObserver().removeOnComputeInternalInsetsListener(this); + getViewTreeObserver().removeOnDrawListener(mSystemGestureExclusionListener); + } + + @Override + public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) { + inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); + mTouchableRegion.setEmpty(); + getTouchableRegion(mTouchableRegion); + inoutInfo.touchableRegion.set(mTouchableRegion); + } + + /** Adds a floating task to the layout. */ + public void addTask(FloatingTasksController.Task task) { + if (task.floatingView == null) return; + + task.floatingView.setTouchHandler(mTouchHandler); + addView(task.floatingView, new LayoutParams(mTaskViewSize.x, mTaskViewSize.y)); + updateTaskViewPosition(task.floatingView); + } + + /** Animates the stashed state of the provided task, if it's part of the floating layer. */ + public void setStashed(FloatingTasksController.Task task, boolean shouldStash) { + if (task.floatingView != null && task.floatingView.getParent() == this) { + mTouchHandler.stashTaskView(task.floatingView, shouldStash); + } + } + + /** Removes all {@link FloatingTaskView} from the layout. */ + public void removeAllTaskViews() { + int childCount = getChildCount(); + ArrayList<View> viewsToRemove = new ArrayList<>(); + for (int i = 0; i < childCount; i++) { + if (getChildAt(i) instanceof FloatingTaskView) { + viewsToRemove.add(getChildAt(i)); + } + } + for (View v : viewsToRemove) { + removeView(v); + } + } + + /** Returns the number of task views in the layout. */ + public int getTaskViewCount() { + int taskViewCount = 0; + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + if (getChildAt(i) instanceof FloatingTaskView) { + taskViewCount++; + } + } + return taskViewCount; + } + + /** + * Called when the task view is un-stuck from the dismiss target. + * @param v the task view being moved. + * @param velX the x velocity of the motion event. + * @param velY the y velocity of the motion event. + * @param wasFlungOut true if the user flung the task view out of the dismiss target (i.e. there + * was an 'up' event), otherwise the user is still dragging. + */ + public void onUnstuckFromTarget(FloatingTaskView v, float velX, float velY, + boolean wasFlungOut) { + mTouchHandler.onUnstuckFromTarget(v, velX, velY, wasFlungOut); + } + + /** + * Updates dimensions and applies them to any task views. + */ + public void updateSizes() { + if (mDismissController != null) { + mDismissController.updateSizes(); + } + + mOverhangWhenStashed = getResources().getDimensionPixelSize( + R.dimen.floating_task_stash_offset); + mVerticalPadding = getResources().getDimensionPixelSize( + R.dimen.floating_task_vertical_padding); + + WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics(); + WindowInsets windowInsets = windowMetrics.getWindowInsets(); + Insets insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars() + | WindowInsets.Type.statusBars() + | WindowInsets.Type.displayCutout()); + Rect bounds = windowMetrics.getBounds(); + mPositionRect.set(bounds.left + insets.left, + bounds.top + insets.top + mVerticalPadding, + bounds.right - insets.right, + bounds.bottom - insets.bottom - mVerticalPadding); + + int taskViewWidth = Math.max(bounds.height(), bounds.width()); + int taskViewHeight = Math.min(bounds.height(), bounds.width()); + taskViewHeight = taskViewHeight - (insets.top + insets.bottom + (mVerticalPadding * 2)); + mTaskViewSize.set((int) (taskViewWidth * START_SIZE_WIDTH_PERCENT), taskViewHeight); + mDefaultStartPosition.set(mPositionRect.left, mPositionRect.top); + + // Update existing views + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + if (getChildAt(i) instanceof FloatingTaskView) { + FloatingTaskView child = (FloatingTaskView) getChildAt(i); + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + lp.width = mTaskViewSize.x; + lp.height = mTaskViewSize.y; + child.setLayoutParams(lp); + updateTaskViewPosition(child); + } + } + } + + /** Returns the first floating task view in the layout. (Currently only ever 1 view). */ + @Nullable + public FloatingTaskView getFirstTaskView() { + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child instanceof FloatingTaskView) { + return (FloatingTaskView) child; + } + } + return null; + } + + private void updateTaskViewPosition(FloatingTaskView floatingView) { + Point lastPosition = mController.getLastPosition(); + if (lastPosition.x == -1 && lastPosition.y == -1) { + floatingView.setX(mDefaultStartPosition.x); + floatingView.setY(mDefaultStartPosition.y); + } else { + floatingView.setX(lastPosition.x); + floatingView.setY(lastPosition.y); + } + if (mTouchHandler.isStashedPosition(floatingView)) { + floatingView.setStashed(true); + } + floatingView.updateLocation(); + } + + /** + * Updates the area of the screen that shouldn't allow the back gesture due to the placement + * of task view (i.e. when task view is stashed on an edge, tapping or swiping that edge would + * un-stash the task view instead of performing the back gesture). + */ + private void updateSystemGestureExclusion() { + Rect excludeZone = mSystemGestureExclusionRects.get(0); + FloatingTaskView floatingTaskView = getFirstTaskView(); + if (floatingTaskView != null && floatingTaskView.isStashed()) { + excludeZone.set(floatingTaskView.getLeft(), + floatingTaskView.getTop(), + floatingTaskView.getRight(), + floatingTaskView.getBottom()); + excludeZone.offset((int) (floatingTaskView.getTranslationX()), + (int) (floatingTaskView.getTranslationY())); + setSystemGestureExclusionRects(mSystemGestureExclusionRects); + } else { + excludeZone.setEmpty(); + setSystemGestureExclusionRects(Collections.emptyList()); + } + } + + /** + * Fills in the touchable region for floating windows. This is used by WindowManager to + * decide which touch events go to the floating windows. + */ + private void getTouchableRegion(Region outRegion) { + int childCount = getChildCount(); + Rect temp = new Rect(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child instanceof FloatingTaskView) { + child.getBoundsOnScreen(temp); + outRegion.op(temp, Region.Op.UNION); + } + } + } + + /** + * Implementation of the touch handler. Animates the task view based on touch events. + */ + private class TouchHandlerImpl implements FloatingTaskTouchHandler { + /** + * The view can be stashed by swiping it towards the current edge or moving it there. If + * the view gets moved in a way that is not one of these gestures, this is flipped to false. + */ + private boolean mCanStash = true; + /** + * This is used to indicate that the view has been un-stuck from the dismiss target and + * needs to spring to the current touch location. + */ + // TODO: implement this behavior + private boolean mSpringToTouchOnNextMotionEvent = false; + + private ArrayList<FlingAnimation> mFlingAnimations; + private ViewPropertyAnimator mViewPropertyAnimation; + + private float mViewInitialX; + private float mViewInitialY; + + private float[] mMinMax = new float[2]; + + @Override + public void onDown(@NonNull FloatingTaskView v, @NonNull MotionEvent ev, float viewInitialX, + float viewInitialY) { + mCanStash = true; + mViewInitialX = viewInitialX; + mViewInitialY = viewInitialY; + mDismissController.setUpMagneticObject(v); + mDismissController.passEventToMagnetizedObject(ev); + } + + @Override + public void onMove(@NonNull FloatingTaskView v, @NonNull MotionEvent ev, + float dx, float dy) { + // Shows the magnetic dismiss target if needed. + mDismissController.showDismiss(/* show= */ true); + + // Send it to magnetic target first. + if (mDismissController.passEventToMagnetizedObject(ev)) { + v.setStashed(false); + mCanStash = true; + + return; + } + + // If we're here magnetic target didn't want it so move as per normal. + + v.setTranslationX(capX(v, mViewInitialX + dx, /* isMoving= */ true)); + v.setTranslationY(capY(v, mViewInitialY + dy)); + if (v.isStashed()) { + // Check if we've moved far enough to be not stashed. + final float centerX = mPositionRect.centerX() - (v.getWidth() / 2f); + final boolean viewInitiallyOnLeftSide = mViewInitialX < centerX; + if (viewInitiallyOnLeftSide) { + if (v.getTranslationX() > mPositionRect.left) { + v.setStashed(false); + mCanStash = true; + } + } else if (v.getTranslationX() + v.getWidth() < mPositionRect.right) { + v.setStashed(false); + mCanStash = true; + } + } + } + + // Reference for math / values: StackAnimationController#flingStackThenSpringToEdge. + // TODO clean up the code here, pretty hard to comprehend + // TODO code here doesn't work the best when in portrait (e.g. can't fling up/down on edges) + @Override + public void onUp(@NonNull FloatingTaskView v, @NonNull MotionEvent ev, + float dx, float dy, float velX, float velY) { + + // Send it to magnetic target first. + if (mDismissController.passEventToMagnetizedObject(ev)) { + v.setStashed(false); + return; + } + mDismissController.showDismiss(/* show= */ false); + + // If we're here magnetic target didn't want it so handle up as per normal. + + final float x = capX(v, mViewInitialX + dx, /* isMoving= */ false); + final float centerX = mPositionRect.centerX(); + final boolean viewInitiallyOnLeftSide = mViewInitialX + v.getWidth() < centerX; + final boolean viewOnLeftSide = x + v.getWidth() < centerX; + final boolean isFling = Math.abs(velX) > ESCAPE_VELOCITY; + final boolean isFlingLeft = isFling && velX < ESCAPE_VELOCITY; + // TODO: check velX here sometimes it doesn't stash on move when I think it should + final boolean shouldStashFromMove = + (velX < 0 && v.getTranslationX() < mPositionRect.left) + || (velX > 0 + && v.getTranslationX() + v.getWidth() > mPositionRect.right); + final boolean shouldStashFromFling = viewInitiallyOnLeftSide == viewOnLeftSide + && isFling + && ((viewOnLeftSide && velX < ESCAPE_VELOCITY) + || (!viewOnLeftSide && velX > ESCAPE_VELOCITY)); + final boolean shouldStash = mCanStash && (shouldStashFromFling || shouldStashFromMove); + + ProtoLog.d(WM_SHELL_FLOATING_APPS, + "shouldStash=%s shouldStashFromFling=%s shouldStashFromMove=%s" + + " viewInitiallyOnLeftSide=%s viewOnLeftSide=%s isFling=%s velX=%f" + + " isStashed=%s", shouldStash, shouldStashFromFling, shouldStashFromMove, + viewInitiallyOnLeftSide, viewOnLeftSide, isFling, velX, v.isStashed()); + + if (v.isStashed()) { + mMinMax[0] = viewOnLeftSide + ? mPositionRect.left - v.getWidth() + mOverhangWhenStashed + : mPositionRect.right - v.getWidth(); + mMinMax[1] = viewOnLeftSide + ? mPositionRect.left + : mPositionRect.right - mOverhangWhenStashed; + } else { + populateMinMax(v, viewOnLeftSide, shouldStash, mMinMax); + } + + boolean movingLeft = isFling ? isFlingLeft : viewOnLeftSide; + float destinationRelativeX = movingLeft + ? mMinMax[0] + : mMinMax[1]; + + // TODO: why is this necessary / when does this happen? + if (mMinMax[1] < v.getTranslationX()) { + mMinMax[1] = v.getTranslationX(); + } + if (v.getTranslationX() < mMinMax[0]) { + mMinMax[0] = v.getTranslationX(); + } + + // Use the touch event's velocity if it's sufficient, otherwise use the minimum velocity + // so that it'll make it all the way to the side of the screen. + final float minimumVelocityToReachEdge = + getMinimumVelocityToReachEdge(v, destinationRelativeX); + final float startXVelocity = movingLeft + ? Math.min(minimumVelocityToReachEdge, velX) + : Math.max(minimumVelocityToReachEdge, velX); + + cancelAnyAnimations(v); + + mFlingAnimations = getAnimationForUpEvent(v, shouldStash, + startXVelocity, mMinMax[0], mMinMax[1], destinationRelativeX); + for (int i = 0; i < mFlingAnimations.size(); i++) { + mFlingAnimations.get(i).start(); + } + } + + @Override + public void onClick(@NonNull FloatingTaskView v) { + if (v.isStashed()) { + final float centerX = mPositionRect.centerX() - (v.getWidth() / 2f); + final boolean viewOnLeftSide = v.getTranslationX() < centerX; + final float destinationRelativeX = viewOnLeftSide + ? mPositionRect.left + : mPositionRect.right - v.getWidth(); + final float minimumVelocityToReachEdge = + getMinimumVelocityToReachEdge(v, destinationRelativeX); + populateMinMax(v, viewOnLeftSide, /* stashed= */ true, mMinMax); + + cancelAnyAnimations(v); + + FlingAnimation flingAnimation = new FlingAnimation(v, + DynamicAnimation.TRANSLATION_X); + flingAnimation.setFriction(FLING_FRICTION) + .setStartVelocity(minimumVelocityToReachEdge) + .setMinValue(mMinMax[0]) + .setMaxValue(mMinMax[1]) + .addEndListener((animation, canceled, value, velocity) -> { + if (canceled) return; + mController.setLastPosition((int) v.getTranslationX(), + (int) v.getTranslationY()); + v.setStashed(false); + v.updateLocation(); + }); + mFlingAnimations = new ArrayList<>(); + mFlingAnimations.add(flingAnimation); + flingAnimation.start(); + } + } + + public void onUnstuckFromTarget(FloatingTaskView v, float velX, float velY, + boolean wasFlungOut) { + if (wasFlungOut) { + snapTaskViewToEdge(v, velX, /* shouldStash= */ false); + } else { + // TODO: use this for something / to spring the view to the touch location + mSpringToTouchOnNextMotionEvent = true; + } + } + + public void stashTaskView(FloatingTaskView v, boolean shouldStash) { + if (v.isStashed() == shouldStash) { + return; + } + final float centerX = mPositionRect.centerX() - (v.getWidth() / 2f); + final boolean viewOnLeftSide = v.getTranslationX() < centerX; + snapTaskViewToEdge(v, viewOnLeftSide ? -ESCAPE_VELOCITY : ESCAPE_VELOCITY, shouldStash); + } + + public boolean isStashedPosition(View v) { + return v.getTranslationX() < mPositionRect.left + || v.getTranslationX() + v.getWidth() > mPositionRect.right; + } + + // TODO: a lot of this is duplicated in onUp -- can it be unified? + private void snapTaskViewToEdge(FloatingTaskView v, float velX, boolean shouldStash) { + final boolean movingLeft = velX < ESCAPE_VELOCITY; + populateMinMax(v, movingLeft, shouldStash, mMinMax); + float destinationRelativeX = movingLeft + ? mMinMax[0] + : mMinMax[1]; + + // TODO: why is this necessary / when does this happen? + if (mMinMax[1] < v.getTranslationX()) { + mMinMax[1] = v.getTranslationX(); + } + if (v.getTranslationX() < mMinMax[0]) { + mMinMax[0] = v.getTranslationX(); + } + + // Use the touch event's velocity if it's sufficient, otherwise use the minimum velocity + // so that it'll make it all the way to the side of the screen. + final float minimumVelocityToReachEdge = + getMinimumVelocityToReachEdge(v, destinationRelativeX); + final float startXVelocity = movingLeft + ? Math.min(minimumVelocityToReachEdge, velX) + : Math.max(minimumVelocityToReachEdge, velX); + + cancelAnyAnimations(v); + + mFlingAnimations = getAnimationForUpEvent(v, + shouldStash, startXVelocity, mMinMax[0], mMinMax[1], + destinationRelativeX); + for (int i = 0; i < mFlingAnimations.size(); i++) { + mFlingAnimations.get(i).start(); + } + } + + private void cancelAnyAnimations(FloatingTaskView v) { + if (mFlingAnimations != null) { + for (int i = 0; i < mFlingAnimations.size(); i++) { + if (mFlingAnimations.get(i).isRunning()) { + mFlingAnimations.get(i).cancel(); + } + } + } + if (mViewPropertyAnimation != null) { + mViewPropertyAnimation.cancel(); + mViewPropertyAnimation = null; + } + } + + private ArrayList<FlingAnimation> getAnimationForUpEvent(FloatingTaskView v, + boolean shouldStash, float startVelX, float minValue, float maxValue, + float destinationRelativeX) { + final float ty = v.getTranslationY(); + final ArrayList<FlingAnimation> animations = new ArrayList<>(); + if (ty != capY(v, ty)) { + // The view was being dismissed so the Y is out of bounds, need to animate that. + FlingAnimation yFlingAnimation = new FlingAnimation(v, + DynamicAnimation.TRANSLATION_Y); + yFlingAnimation.setFriction(FLING_FRICTION) + .setStartVelocity(startVelX) + .setMinValue(mPositionRect.top) + .setMaxValue(mPositionRect.bottom - mTaskViewSize.y); + animations.add(yFlingAnimation); + } + FlingAnimation flingAnimation = new FlingAnimation(v, DynamicAnimation.TRANSLATION_X); + flingAnimation.setFriction(FLING_FRICTION) + .setStartVelocity(startVelX) + .setMinValue(minValue) + .setMaxValue(maxValue) + .addEndListener((animation, canceled, value, velocity) -> { + if (canceled) return; + Runnable endAction = () -> { + v.setStashed(shouldStash); + v.updateLocation(); + if (!v.isStashed()) { + mController.setLastPosition((int) v.getTranslationX(), + (int) v.getTranslationY()); + } + }; + if (!shouldStash) { + final int xTranslation = (int) v.getTranslationX(); + if (xTranslation != destinationRelativeX) { + // TODO: this animation doesn't feel great, should figure out + // a better way to do this or remove the need for it all together. + mViewPropertyAnimation = v.animate() + .translationX(destinationRelativeX) + .setListener(getAnimationListener(endAction)); + mViewPropertyAnimation.start(); + } else { + endAction.run(); + } + } else { + endAction.run(); + } + }); + animations.add(flingAnimation); + return animations; + } + + private AnimatorListenerAdapter getAnimationListener(Runnable endAction) { + return new AnimatorListenerAdapter() { + boolean translationCanceled = false; + @Override + public void onAnimationCancel(Animator animation) { + super.onAnimationCancel(animation); + translationCanceled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (!translationCanceled) { + endAction.run(); + } + } + }; + } + + private void populateMinMax(FloatingTaskView v, boolean onLeft, boolean shouldStash, + float[] out) { + if (shouldStash) { + out[0] = onLeft + ? mPositionRect.left - v.getWidth() + mOverhangWhenStashed + : mPositionRect.right - v.getWidth(); + out[1] = onLeft + ? mPositionRect.left + : mPositionRect.right - mOverhangWhenStashed; + } else { + out[0] = mPositionRect.left; + out[1] = mPositionRect.right - mTaskViewSize.x; + } + } + + private float getMinimumVelocityToReachEdge(FloatingTaskView v, + float destinationRelativeX) { + // Minimum velocity required for the view to make it to the targeted side of the screen, + // taking friction into account (4.2f is the number that friction scalars are multiplied + // by in DynamicAnimation.DragForce). This is an estimate and could be slightly off, the + // animation at the end will ensure that it reaches the destination X regardless. + return (destinationRelativeX - v.getTranslationX()) * (FLING_FRICTION * 4.2f); + } + + private float capX(FloatingTaskView v, float x, boolean isMoving) { + final int width = v.getWidth(); + if (v.isStashed() || isMoving) { + if (x < mPositionRect.left - v.getWidth() + mOverhangWhenStashed) { + return mPositionRect.left - v.getWidth() + mOverhangWhenStashed; + } + if (x > mPositionRect.right - mOverhangWhenStashed) { + return mPositionRect.right - mOverhangWhenStashed; + } + } else { + if (x < mPositionRect.left) { + return mPositionRect.left; + } + if (x > mPositionRect.right - width) { + return mPositionRect.right - width; + } + } + return x; + } + + private float capY(FloatingTaskView v, float y) { + final int height = v.getHeight(); + if (y < mPositionRect.top) { + return mPositionRect.top; + } + if (y > mPositionRect.bottom - height) { + return mPositionRect.bottom - height; + } + return y; + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskView.java new file mode 100644 index 000000000000..581204a82ec7 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskView.java @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2022 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.wm.shell.floating.views; + +import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; +import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; + +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FLOATING_APPS; + +import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.app.ActivityTaskManager; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.Outline; +import android.graphics.Rect; +import android.os.RemoteException; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewOutlineProvider; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; + +import com.android.internal.policy.ScreenDecorationsUtils; +import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.R; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.TaskView; +import com.android.wm.shell.TaskViewTransitions; +import com.android.wm.shell.bubbles.RelativeTouchListener; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.common.annotations.ShellMainThread; +import com.android.wm.shell.floating.FloatingTasksController; + +/** + * A view that holds a floating task using {@link TaskView} along with additional UI to manage + * the task. + */ +public class FloatingTaskView extends FrameLayout { + + private static final String TAG = FloatingTaskView.class.getSimpleName(); + + private FloatingTasksController mController; + + private FloatingMenuView mMenuView; + private int mMenuHeight; + private TaskView mTaskView; + + private float mCornerRadius = 0f; + private int mBackgroundColor; + + private FloatingTasksController.Task mTask; + + private boolean mIsStashed; + + /** + * Creates a floating task view. + * + * @param context the context to use. + * @param controller the controller to notify about changes in the floating task (e.g. removal). + */ + public FloatingTaskView(Context context, FloatingTasksController controller) { + super(context); + mController = controller; + setElevation(getResources().getDimensionPixelSize(R.dimen.floating_task_elevation)); + mMenuHeight = context.getResources().getDimensionPixelSize(R.dimen.floating_task_menu_size); + mMenuView = new FloatingMenuView(context); + addView(mMenuView); + + applyThemeAttrs(); + + setClipToOutline(true); + setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius); + } + }); + } + + // TODO: call this when theme/config changes + void applyThemeAttrs() { + boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows( + mContext.getResources()); + final TypedArray ta = mContext.obtainStyledAttributes(new int[] { + android.R.attr.dialogCornerRadius, + android.R.attr.colorBackgroundFloating}); + mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0; + mCornerRadius = mCornerRadius / 2f; + mBackgroundColor = ta.getColor(1, Color.WHITE); + + ta.recycle(); + + mMenuView.setCornerRadius(mCornerRadius); + mMenuHeight = getResources().getDimensionPixelSize( + R.dimen.floating_task_menu_size); + + if (mTaskView != null) { + mTaskView.setCornerRadius(mCornerRadius); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int height = MeasureSpec.getSize(heightMeasureSpec); + + // Add corner radius here so that the menu extends behind the rounded corners of TaskView. + int menuViewHeight = Math.min((int) (mMenuHeight + mCornerRadius), height); + measureChild(mMenuView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight, + MeasureSpec.getMode(heightMeasureSpec))); + + if (mTaskView != null) { + int taskViewHeight = height - menuViewHeight; + measureChild(mTaskView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(taskViewHeight, + MeasureSpec.getMode(heightMeasureSpec))); + } + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + // Drag handle above + final int dragHandleBottom = t + mMenuView.getMeasuredHeight(); + mMenuView.layout(l, t, r, dragHandleBottom); + if (mTaskView != null) { + // Subtract radius so that the menu extends behind the rounded corners of TaskView. + mTaskView.layout(l, (int) (dragHandleBottom - mCornerRadius), r, + dragHandleBottom + mTaskView.getMeasuredHeight()); + } + } + + /** + * Constructs the TaskView to display the task. Must be called for {@link #startTask} to work. + */ + public void createTaskView(Context context, ShellTaskOrganizer organizer, + TaskViewTransitions transitions, SyncTransactionQueue syncQueue) { + mTaskView = new TaskView(context, organizer, transitions, syncQueue); + addView(mTaskView); + mTaskView.setEnableSurfaceClipping(true); + mTaskView.setCornerRadius(mCornerRadius); + } + + /** + * Starts the provided task in the TaskView, if the TaskView exists. This should be called after + * {@link #createTaskView}. + */ + public void startTask(@ShellMainThread ShellExecutor executor, + FloatingTasksController.Task task) { + if (mTaskView == null) { + Log.e(TAG, "starting task before creating the view!"); + return; + } + mTask = task; + mTaskView.setListener(executor, mTaskViewListener); + } + + /** + * Sets the touch handler for the view. + * + * @param handler the touch handler for the view. + */ + public void setTouchHandler(FloatingTaskLayer.FloatingTaskTouchHandler handler) { + setOnTouchListener(new RelativeTouchListener() { + @Override + public boolean onDown(@NonNull View v, @NonNull MotionEvent ev) { + handler.onDown(FloatingTaskView.this, ev, v.getTranslationX(), v.getTranslationY()); + return true; + } + + @Override + public void onMove(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX, + float viewInitialY, float dx, float dy) { + handler.onMove(FloatingTaskView.this, ev, dx, dy); + } + + @Override + public void onUp(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX, + float viewInitialY, float dx, float dy, float velX, float velY) { + handler.onUp(FloatingTaskView.this, ev, dx, dy, velX, velY); + } + }); + setOnClickListener(view -> { + handler.onClick(FloatingTaskView.this); + }); + + mMenuView.addMenuItem(null, view -> { + if (mIsStashed) { + // If we're stashed all clicks un-stash. + handler.onClick(FloatingTaskView.this); + } + }); + } + + private void setContentVisibility(boolean visible) { + if (mTaskView == null) return; + mTaskView.setAlpha(visible ? 1f : 0f); + } + + /** + * Sets the alpha of both this view and the TaskView. + */ + public void setTaskViewAlpha(float alpha) { + if (mTaskView != null) { + mTaskView.setAlpha(alpha); + } + setAlpha(alpha); + } + + /** + * Call when the location or size of the view has changed to update TaskView. + */ + public void updateLocation() { + if (mTaskView == null) return; + mTaskView.onLocationChanged(); + } + + private void updateMenuColor() { + ActivityManager.RunningTaskInfo info = mTaskView.getTaskInfo(); + int color = info != null ? info.taskDescription.getBackgroundColor() : -1; + if (color != -1) { + mMenuView.setBackgroundColor(color); + } else { + mMenuView.setBackgroundColor(mBackgroundColor); + } + } + + /** + * Sets whether the view is stashed or not. + * + * Also updates the touchable area based on this. If the view is stashed we don't direct taps + * on the activity to the activity, instead a tap will un-stash the view. + */ + public void setStashed(boolean isStashed) { + if (mIsStashed != isStashed) { + mIsStashed = isStashed; + if (mTaskView == null) { + return; + } + updateObscuredTouchRect(); + } + } + + /** Whether the view is stashed at the edge of the screen or not. **/ + public boolean isStashed() { + return mIsStashed; + } + + private void updateObscuredTouchRect() { + if (mIsStashed) { + Rect tmpRect = new Rect(); + getBoundsOnScreen(tmpRect); + mTaskView.setObscuredTouchRect(tmpRect); + } else { + mTaskView.setObscuredTouchRect(null); + } + } + + /** + * Whether the task needs to be restarted, this can happen when {@link #cleanUpTaskView()} has + * been called on this view or if + * {@link #startTask(ShellExecutor, FloatingTasksController.Task)} was never called. + */ + public boolean needsTaskStarted() { + // If the task needs to be restarted then TaskView would have been cleaned up. + return mTaskView == null; + } + + /** Call this when the floating task activity is no longer in use. */ + public void cleanUpTaskView() { + if (mTask != null && mTask.taskId != INVALID_TASK_ID) { + try { + ActivityTaskManager.getService().removeTask(mTask.taskId); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage()); + } + } + if (mTaskView != null) { + mTaskView.release(); + removeView(mTaskView); + mTaskView = null; + } + } + + // TODO: use task background colour / how to get the taskInfo ? + private static int getDragBarColor(ActivityManager.RunningTaskInfo taskInfo) { + final int taskBgColor = taskInfo.taskDescription.getStatusBarColor(); + return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb(); + } + + private final TaskView.Listener mTaskViewListener = new TaskView.Listener() { + private boolean mInitialized = false; + private boolean mDestroyed = false; + + @Override + public void onInitialized() { + if (mDestroyed || mInitialized) { + return; + } + // Custom options so there is no activity transition animation + ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(), + /* enterResId= */ 0, /* exitResId= */ 0); + + Rect launchBounds = new Rect(); + mTaskView.getBoundsOnScreen(launchBounds); + + try { + options.setTaskAlwaysOnTop(true); + if (mTask.intent != null) { + Intent fillInIntent = new Intent(); + // Apply flags to make behaviour match documentLaunchMode=always. + fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT); + fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); + + PendingIntent pi = PendingIntent.getActivity(mContext, 0, mTask.intent, + PendingIntent.FLAG_MUTABLE, + null); + mTaskView.startActivity(pi, fillInIntent, options, launchBounds); + } else { + ProtoLog.e(WM_SHELL_FLOATING_APPS, "Tried to start a task with null intent"); + } + } catch (RuntimeException e) { + ProtoLog.e(WM_SHELL_FLOATING_APPS, "Exception while starting task: %s", + e.getMessage()); + mController.removeTask(); + } + mInitialized = true; + } + + @Override + public void onReleased() { + mDestroyed = true; + } + + @Override + public void onTaskCreated(int taskId, ComponentName name) { + mTask.taskId = taskId; + updateMenuColor(); + setContentVisibility(true); + } + + @Override + public void onTaskVisibilityChanged(int taskId, boolean visible) { + setContentVisibility(visible); + } + + @Override + public void onTaskRemovalStarted(int taskId) { + // Must post because this is called from a binder thread. + post(() -> { + mController.removeTask(); + cleanUpTaskView(); + }); + } + + @Override + public void onBackPressedOnTaskRoot(int taskId) { + if (mTask.taskId == taskId && !mIsStashed) { + // TODO: is removing the window the desired behavior? + post(() -> mController.removeTask()); + } + } + }; +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java index f58719b225a4..e2d5a499d1e1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java @@ -28,7 +28,7 @@ import androidx.annotation.Nullable; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.desktopmode.DesktopMode; +import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.sysui.ShellInit; @@ -90,7 +90,7 @@ public class FreeformTaskListener<T extends AutoCloseable> t.apply(); } - if (DesktopMode.IS_SUPPORTED && taskInfo.isVisible) { + if (DesktopModeStatus.IS_SUPPORTED && taskInfo.isVisible) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Adding active freeform task: #%d", taskInfo.taskId); mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTask(taskInfo.taskId)); @@ -123,7 +123,7 @@ public class FreeformTaskListener<T extends AutoCloseable> taskInfo.taskId); mTasks.remove(taskInfo.taskId); - if (DesktopMode.IS_SUPPORTED) { + if (DesktopModeStatus.IS_SUPPORTED) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Removing active freeform task: #%d", taskInfo.taskId); mDesktopModeTaskRepository.ifPresent(it -> it.removeActiveTask(taskInfo.taskId)); @@ -150,7 +150,7 @@ public class FreeformTaskListener<T extends AutoCloseable> mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration); } - if (DesktopMode.IS_SUPPORTED) { + if (DesktopModeStatus.IS_SUPPORTED) { if (taskInfo.isVisible) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Adding active freeform task: #%d", taskInfo.taskId); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index b32c3eed2fb4..6728c00af51b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -195,6 +195,17 @@ public class PipAnimationController { } /** + * Returns true if the PiP window is currently being animated. + */ + public boolean isAnimating() { + PipAnimationController.PipTransitionAnimator animator = getCurrentAnimator(); + if (animator != null && animator.isRunning()) { + return true; + } + return false; + } + + /** * Quietly cancel the animator by removing the listeners first. */ static void quietCancel(@NonNull ValueAnimator animator) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 1a52d8c395ba..f170e774739f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -324,19 +324,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return mPipTransitionController; } - /** - * Returns true if the PiP window is currently being animated. - */ - public boolean isAnimating() { - // TODO(b/183746978) move this to PipAnimationController, and inject that in PipController - PipAnimationController.PipTransitionAnimator animator = - mPipAnimationController.getCurrentAnimator(); - if (animator != null && animator.isRunning()) { - return true; - } - return false; - } - public Rect getCurrentOrAnimatingBounds() { PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getCurrentAnimator(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java index 29434f73e84b..fa0061982c45 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java @@ -83,7 +83,9 @@ public class PipUtils { public static boolean remoteActionsMatch(RemoteAction action1, RemoteAction action2) { if (action1 == action2) return true; if (action1 == null || action2 == null) return false; - return Objects.equals(action1.getTitle(), action2.getTitle()) + return action1.isEnabled() == action2.isEnabled() + && action1.shouldShowIcon() == action2.shouldShowIcon() + && Objects.equals(action1.getTitle(), action2.getTitle()) && Objects.equals(action1.getContentDescription(), action2.getContentDescription()) && Objects.equals(action1.getActionIntent(), action2.getActionIntent()); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index bc8191d2af46..af47666efa5a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -130,6 +130,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb private DisplayController mDisplayController; private PipInputConsumer mPipInputConsumer; private WindowManagerShellWrapper mWindowManagerShellWrapper; + private PipAnimationController mPipAnimationController; private PipAppOpsListener mAppOpsListener; private PipMediaController mMediaController; private PipBoundsAlgorithm mPipBoundsAlgorithm; @@ -158,7 +159,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb return; } // if there is another animation ongoing, wait for it to finish and try again - if (mPipTaskOrganizer.isAnimating()) { + if (mPipAnimationController.isAnimating()) { mMainExecutor.removeCallbacks( mMovePipInResponseToKeepClearAreasChangeCallback); mMainExecutor.executeDelayed( @@ -368,6 +369,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb ShellCommandHandler shellCommandHandler, ShellController shellController, DisplayController displayController, + PipAnimationController pipAnimationController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, PipKeepClearAlgorithm pipKeepClearAlgorithm, @@ -392,11 +394,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb } return new PipController(context, shellInit, shellCommandHandler, shellController, - displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm, - pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController, - pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController, - windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder, - displayInsetsController, oneHandedController, mainExecutor) + displayController, pipAnimationController, pipAppOpsListener, + pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, + pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState, + pipTouchHandler, pipTransitionController, windowManagerShellWrapper, + taskStackListener, pipParamsChangedForwarder, displayInsetsController, + oneHandedController, mainExecutor) .mImpl; } @@ -405,6 +408,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb ShellCommandHandler shellCommandHandler, ShellController shellController, DisplayController displayController, + PipAnimationController pipAnimationController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, PipKeepClearAlgorithm pipKeepClearAlgorithm, @@ -445,6 +449,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb mMediaController = pipMediaController; mMenuController = phonePipMenuController; mTouchHandler = pipTouchHandler; + mPipAnimationController = pipAnimationController; mAppOpsListener = pipAppOpsListener; mOneHandedController = oneHandedController; mPipTransitionController = pipTransitionController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java index 3fef82352728..c52ed249c2ca 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java @@ -48,6 +48,8 @@ public enum ShellProtoLogGroup implements IProtoLogGroup { Consts.TAG_WM_SHELL), WM_SHELL_DESKTOP_MODE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM_SHELL), + WM_SHELL_FLOATING_APPS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM_SHELL), TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest"); private final boolean mEnabled; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java index a5748f69388f..2a625524b48b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java @@ -17,6 +17,11 @@ package com.android.wm.shell.recents; import com.android.wm.shell.common.annotations.ExternalThread; +import com.android.wm.shell.util.GroupedRecentTaskInfo; + +import java.util.List; +import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Interface for interacting with the recent tasks. @@ -29,4 +34,11 @@ public interface RecentTasks { default IRecentTasks createExternalInterface() { return null; } + + /** + * Gets the set of recent tasks. + */ + default void getRecentTasks(int maxNum, int flags, int userId, Executor callbackExecutor, + Consumer<List<GroupedRecentTaskInfo>> callback) { + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index ff4b2edb7310..02b5a35f653b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -24,6 +24,7 @@ import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTas import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.TaskInfo; +import android.content.ComponentName; import android.content.Context; import android.os.RemoteException; import android.util.Slog; @@ -43,7 +44,7 @@ import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.common.annotations.ShellMainThread; -import com.android.wm.shell.desktopmode.DesktopMode; +import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.sysui.ShellCommandHandler; @@ -57,6 +58,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Manages the recent task list from the system, caching it as necessary. @@ -71,6 +74,7 @@ public class RecentTasksController implements TaskStackListenerCallback, private final ShellExecutor mMainExecutor; private final TaskStackListenerImpl mTaskStackListener; private final RecentTasks mImpl = new RecentTasksImpl(); + private final ActivityTaskManager mActivityTaskManager; private IRecentTasksListener mListener; private final boolean mIsDesktopMode; @@ -95,6 +99,7 @@ public class RecentTasksController implements TaskStackListenerCallback, ShellInit shellInit, ShellCommandHandler shellCommandHandler, TaskStackListenerImpl taskStackListener, + ActivityTaskManager activityTaskManager, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, @ShellMainThread ShellExecutor mainExecutor ) { @@ -102,17 +107,19 @@ public class RecentTasksController implements TaskStackListenerCallback, return null; } return new RecentTasksController(context, shellInit, shellCommandHandler, taskStackListener, - desktopModeTaskRepository, mainExecutor); + activityTaskManager, desktopModeTaskRepository, mainExecutor); } RecentTasksController(Context context, ShellInit shellInit, ShellCommandHandler shellCommandHandler, TaskStackListenerImpl taskStackListener, + ActivityTaskManager activityTaskManager, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, ShellExecutor mainExecutor) { mContext = context; mShellCommandHandler = shellCommandHandler; + mActivityTaskManager = activityTaskManager; mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC); mTaskStackListener = taskStackListener; mDesktopModeTaskRepository = desktopModeTaskRepository; @@ -269,15 +276,10 @@ public class RecentTasksController implements TaskStackListenerCallback, } @VisibleForTesting - List<ActivityManager.RecentTaskInfo> getRawRecentTasks(int maxNum, int flags, int userId) { - return ActivityTaskManager.getInstance().getRecentTasks(maxNum, flags, userId); - } - - @VisibleForTesting ArrayList<GroupedRecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) { // Note: the returned task list is from the most-recent to least-recent order - final List<ActivityManager.RecentTaskInfo> rawList = getRawRecentTasks(maxNum, flags, - userId); + final List<ActivityManager.RecentTaskInfo> rawList = mActivityTaskManager.getRecentTasks( + maxNum, flags, userId); // Make a mapping of task id -> task info final SparseArray<ActivityManager.RecentTaskInfo> rawMapping = new SparseArray<>(); @@ -286,7 +288,7 @@ public class RecentTasksController implements TaskStackListenerCallback, rawMapping.put(taskInfo.taskId, taskInfo); } - boolean desktopModeActive = DesktopMode.isActive(mContext); + boolean desktopModeActive = DesktopModeStatus.isActive(mContext); ArrayList<ActivityManager.RecentTaskInfo> freeformTasks = new ArrayList<>(); // Pull out the pairs as we iterate back in the list @@ -319,7 +321,6 @@ public class RecentTasksController implements TaskStackListenerCallback, // Add a special entry for freeform tasks if (!freeformTasks.isEmpty()) { - // First task is added separately recentTasks.add(0, GroupedRecentTaskInfo.forFreeformTasks( freeformTasks.toArray(new ActivityManager.RecentTaskInfo[0]))); } @@ -327,6 +328,29 @@ public class RecentTasksController implements TaskStackListenerCallback, return recentTasks; } + /** + * Find the background task that match the given component. + */ + @Nullable + public ActivityManager.RecentTaskInfo findTaskInBackground(ComponentName componentName) { + if (componentName == null) { + return null; + } + List<ActivityManager.RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks( + Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE, + ActivityManager.getCurrentUser()); + for (int i = 0; i < tasks.size(); i++) { + final ActivityManager.RecentTaskInfo task = tasks.get(i); + if (task.isVisible) { + continue; + } + if (componentName.equals(task.baseIntent.getComponent())) { + return task; + } + } + return null; + } + public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); @@ -352,6 +376,16 @@ public class RecentTasksController implements TaskStackListenerCallback, mIRecentTasks = new IRecentTasksImpl(RecentTasksController.this); return mIRecentTasks; } + + @Override + public void getRecentTasks(int maxNum, int flags, int userId, Executor executor, + Consumer<List<GroupedRecentTaskInfo>> callback) { + mMainExecutor.execute(() -> { + List<GroupedRecentTaskInfo> tasks = + RecentTasksController.this.getRecentTasks(maxNum, flags, userId); + executor.execute(() -> callback.accept(tasks)); + }); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 991f136c0055..07a6895e2720 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -385,6 +385,9 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } @Override public void onAnimationCancelled(boolean isKeyguardOccluded) { + final WindowContainerTransaction evictWct = new WindowContainerTransaction(); + mStageCoordinator.prepareEvictInvisibleChildTasks(evictWct); + mSyncQueue.queue(evictWct); } }; options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, @@ -472,8 +475,16 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION); // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the - // split. + // split and there is no reusable background task. if (shouldAddMultipleTaskFlag(intent.getIntent(), position)) { + final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional.isPresent() + ? mRecentTasksOptional.get().findTaskInBackground( + intent.getIntent().getComponent()) + : null; + if (taskInfo != null) { + startTask(taskInfo.taskId, position, options); + return; + } fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK"); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java index 033d743d8042..2dc4a0441b06 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java @@ -87,6 +87,10 @@ public class SplitscreenEventLogger { return mLoggerSessionId != null; } + public boolean isEnterRequestedByDrag() { + return mEnterReason == ENTER_REASON_DRAG; + } + /** * May be called before logEnter() to indicate that the session was started from a drag. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index c8dcf4acd746..c17f8226c925 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -18,6 +18,8 @@ package com.android.wm.shell.splitscreen; import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED; +import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; @@ -503,6 +505,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final WindowContainerTransaction wct = new WindowContainerTransaction(); options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct); + // If split still not active, apply windows bounds first to avoid surface reset to + // wrong pos by SurfaceAnimator from wms. + if (!mMainStage.isActive() && mLogger.isEnterRequestedByDrag()) { + updateWindowBounds(mSplitLayout, wct); + } + wct.sendPendingIntent(intent, fillInIntent, options); mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct); } @@ -1109,6 +1117,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void addActivityOptions(Bundle opts, StageTaskListener stage) { opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token); + // Put BAL flags to avoid activity start aborted. Otherwise, flows like shortcut to split + // will be canceled. + opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true); + opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true); } void updateActivityOptions(Bundle opts, @SplitPosition int position) { @@ -1455,18 +1467,27 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } } else if (isSideStage && hasChildren && !mMainStage.isActive()) { - // TODO (b/238697912) : Add the validation to prevent entering non-recovered status - onSplitScreenEnter(); final WindowContainerTransaction wct = new WindowContainerTransaction(); mSplitLayout.init(); - mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); - mMainStage.activate(wct, true /* includingTopTask */); - updateWindowBounds(mSplitLayout, wct); - wct.reorder(mRootTaskInfo.token, true); - wct.setForceTranslucent(mRootTaskInfo.token, false); + if (mLogger.isEnterRequestedByDrag()) { + prepareEnterSplitScreen(wct); + } else { + // TODO (b/238697912) : Add the validation to prevent entering non-recovered status + onSplitScreenEnter(); + mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); + mMainStage.activate(wct, true /* includingTopTask */); + updateWindowBounds(mSplitLayout, wct); + wct.reorder(mRootTaskInfo.token, true); + wct.setForceTranslucent(mRootTaskInfo.token, false); + } + mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { - mSplitLayout.flingDividerToCenter(); + if (mLogger.isEnterRequestedByDrag()) { + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); + } else { + mSplitLayout.flingDividerToCenter(); + } }); } if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index e8a2cb160880..8b36db9204ac 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -16,6 +16,7 @@ package com.android.wm.shell.windowdecor; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -36,7 +37,7 @@ import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.desktopmode.DesktopMode; +import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.transition.Transitions; @@ -239,7 +240,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true; - return DesktopMode.IS_SUPPORTED + return DesktopModeStatus.IS_SUPPORTED + && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD && mDisplayController.getDisplayContext(taskInfo.displayId) .getResources().getConfiguration().smallestScreenWidthDp >= 600; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index 5040bc37c614..733f6b7d5dbf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -34,7 +34,7 @@ import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.desktopmode.DesktopMode; +import com.android.wm.shell.desktopmode.DesktopModeStatus; /** * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with @@ -164,7 +164,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL View caption = mResult.mRootView.findViewById(R.id.caption); caption.setOnTouchListener(mOnCaptionTouchListener); View maximize = caption.findViewById(R.id.maximize_window); - if (DesktopMode.IS_SUPPORTED) { + if (DesktopModeStatus.IS_SUPPORTED) { // Hide maximize button when desktop mode is available maximize.setVisibility(View.GONE); } else { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java index c0720cf04028..3672ae386dc4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java @@ -44,6 +44,7 @@ public final class TestRunningTaskInfoBuilder { private ActivityManager.TaskDescription.Builder mTaskDescriptionBuilder = null; private final Point mPositionInParent = new Point(); private boolean mIsVisible = false; + private long mLastActiveTime; public static WindowContainerToken createMockWCToken() { final IWindowContainerToken itoken = mock(IWindowContainerToken.class); @@ -52,6 +53,11 @@ public final class TestRunningTaskInfoBuilder { return new WindowContainerToken(itoken); } + public TestRunningTaskInfoBuilder setToken(WindowContainerToken token) { + mToken = token; + return this; + } + public TestRunningTaskInfoBuilder setBounds(Rect bounds) { mBounds.set(bounds); return this; @@ -95,6 +101,11 @@ public final class TestRunningTaskInfoBuilder { return this; } + public TestRunningTaskInfoBuilder setLastActiveTime(long lastActiveTime) { + mLastActiveTime = lastActiveTime; + return this; + } + public ActivityManager.RunningTaskInfo build() { final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); info.taskId = sNextTaskId++; @@ -110,6 +121,7 @@ public final class TestRunningTaskInfoBuilder { mTaskDescriptionBuilder != null ? mTaskDescriptionBuilder.build() : null; info.positionInParent = mPositionInParent; info.isVisible = mIsVisible; + info.lastActiveTime = mLastActiveTime; return info; } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java index a7234c1d3cb8..98b59126227c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java @@ -18,7 +18,9 @@ package com.android.wm.shell.activityembedding; import static android.view.WindowManager.TRANSIT_OPEN; import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY; +import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; @@ -27,6 +29,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import android.animation.Animator; import android.window.TransitionInfo; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -76,4 +79,18 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim verify(mController).onAnimationFinished(mTransition); } + + @Test + public void testChangesBehindStartingWindow() { + final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0); + final TransitionInfo.Change embeddingChange = createChange(); + embeddingChange.setFlags(FLAG_IS_BEHIND_STARTING_WINDOW); + info.addChange(embeddingChange); + final Animator animator = mAnimRunner.createAnimator( + info, mStartTransaction, mFinishTransaction, + () -> mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */)); + + // The animation should be empty when it is behind starting window. + assertEquals(0, animator.getDuration()); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java index c628f3994d8d..dd23d97d9199 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java @@ -19,16 +19,22 @@ package com.android.wm.shell.desktopmode; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.WindowConfiguration; +import android.app.ActivityManager; import android.os.Handler; import android.os.IBinder; import android.testing.AndroidTestingRunner; @@ -39,13 +45,17 @@ import android.window.WindowContainerTransaction.Change; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.TestRunningTaskInfoBuilder; +import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -67,18 +77,38 @@ public class DesktopModeControllerTest extends ShellTestCase { private Handler mMockHandler; @Mock private Transitions mMockTransitions; + private TestShellExecutor mExecutor; private DesktopModeController mController; + private DesktopModeTaskRepository mDesktopModeTaskRepository; private ShellInit mShellInit; + private StaticMockitoSession mMockitoSession; @Before public void setUp() { + mMockitoSession = mockitoSession().mockStatic(DesktopModeStatus.class).startMocking(); + when(DesktopModeStatus.isActive(any())).thenReturn(true); + mShellInit = Mockito.spy(new ShellInit(mTestExecutor)); + mExecutor = new TestShellExecutor(); + + mDesktopModeTaskRepository = new DesktopModeTaskRepository(); mController = new DesktopModeController(mContext, mShellInit, mShellTaskOrganizer, - mRootTaskDisplayAreaOrganizer, mMockHandler, mMockTransitions); + mRootTaskDisplayAreaOrganizer, mMockTransitions, + mDesktopModeTaskRepository, mMockHandler, mExecutor); + + when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(anyInt())).thenReturn( + new WindowContainerTransaction()); mShellInit.init(); + clearInvocations(mShellTaskOrganizer); + clearInvocations(mRootTaskDisplayAreaOrganizer); + } + + @After + public void tearDown() { + mMockitoSession.finishMocking(); } @Test @@ -159,17 +189,15 @@ public class DesktopModeControllerTest extends ShellTestCase { assertThat(wct.getChanges()).hasSize(3); // Verify executed WCT has a change for setting task windowing mode to undefined - Change taskWmModeChange = wct.getChanges().get(taskWmMockToken.binder()); - assertThat(taskWmModeChange).isNotNull(); - assertThat(taskWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED); + Change taskWmMode = wct.getChanges().get(taskWmMockToken.binder()); + assertThat(taskWmMode).isNotNull(); + assertThat(taskWmMode.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED); // Verify executed WCT has a change for clearing task bounds - Change taskBoundsChange = wct.getChanges().get(taskBoundsMockToken.binder()); - assertThat(taskBoundsChange).isNotNull(); - assertThat(taskBoundsChange.getWindowSetMask() - & WindowConfiguration.WINDOW_CONFIG_BOUNDS).isNotEqualTo(0); - assertThat(taskBoundsChange.getConfiguration().windowConfiguration.getBounds().isEmpty()) - .isTrue(); + Change bounds = wct.getChanges().get(taskBoundsMockToken.binder()); + assertThat(bounds).isNotNull(); + assertThat(bounds.getWindowSetMask() & WINDOW_CONFIG_BOUNDS).isNotEqualTo(0); + assertThat(bounds.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue(); // Verify executed WCT has a change for setting display windowing mode to fullscreen Change displayWmModeChange = wct.getChanges().get(displayAreaInfo.token.asBinder()); @@ -177,6 +205,41 @@ public class DesktopModeControllerTest extends ShellTestCase { assertThat(displayWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN); } + @Test + public void testShowDesktopApps() { + // Set up two active tasks on desktop + mDesktopModeTaskRepository.addActiveTask(1); + mDesktopModeTaskRepository.addActiveTask(2); + MockToken token1 = new MockToken(); + MockToken token2 = new MockToken(); + ActivityManager.RunningTaskInfo taskInfo1 = new TestRunningTaskInfoBuilder().setToken( + token1.token()).setLastActiveTime(100).build(); + ActivityManager.RunningTaskInfo taskInfo2 = new TestRunningTaskInfoBuilder().setToken( + token2.token()).setLastActiveTime(200).build(); + when(mShellTaskOrganizer.getRunningTaskInfo(1)).thenReturn(taskInfo1); + when(mShellTaskOrganizer.getRunningTaskInfo(2)).thenReturn(taskInfo2); + + // Run show desktop apps logic + mController.showDesktopApps(); + ArgumentCaptor<WindowContainerTransaction> wctCaptor = ArgumentCaptor.forClass( + WindowContainerTransaction.class); + verify(mShellTaskOrganizer).applyTransaction(wctCaptor.capture()); + WindowContainerTransaction wct = wctCaptor.getValue(); + + // Check wct has reorder calls + assertThat(wct.getHierarchyOps()).hasSize(2); + + // Task 2 has activity later, must be first + WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0); + assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); + assertThat(op1.getContainer()).isEqualTo(token2.binder()); + + // Task 1 should be second + WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(0); + assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); + assertThat(op2.getContainer()).isEqualTo(token2.binder()); + } + private static class MockToken { private final WindowContainerToken mToken; private final IBinder mBinder; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java new file mode 100644 index 000000000000..a88c83779f25 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2022 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.wm.shell.floating; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.wm.shell.floating.FloatingTasksController.SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Intent; +import android.content.res.Configuration; +import android.graphics.Insets; +import android.graphics.Rect; +import android.os.RemoteException; +import android.os.SystemProperties; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowMetrics; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.TaskViewTransitions; +import com.android.wm.shell.TestShellExecutor; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.floating.views.FloatingTaskLayer; +import com.android.wm.shell.sysui.ShellCommandHandler; +import com.android.wm.shell.sysui.ShellController; +import com.android.wm.shell.sysui.ShellInit; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Optional; + +/** + * Tests for the floating tasks controller. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class FloatingTasksControllerTest extends ShellTestCase { + // Some behavior in the controller constructor is dependent on this so we can only + // validate if it's working for the real value for those things. + private static final boolean FLOATING_TASKS_ACTUALLY_ENABLED = + SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false); + + @Mock private ShellInit mShellInit; + @Mock private ShellController mShellController; + @Mock private WindowManager mWindowManager; + @Mock private ShellTaskOrganizer mTaskOrganizer; + @Captor private ArgumentCaptor<FloatingTaskLayer> mFloatingTaskLayerCaptor; + + private FloatingTasksController mController; + + @Before + public void setUp() throws RemoteException { + MockitoAnnotations.initMocks(this); + + WindowMetrics windowMetrics = mock(WindowMetrics.class); + WindowInsets windowInsets = mock(WindowInsets.class); + Insets insets = Insets.of(0, 0, 0, 0); + when(mWindowManager.getCurrentWindowMetrics()).thenReturn(windowMetrics); + when(windowMetrics.getWindowInsets()).thenReturn(windowInsets); + when(windowMetrics.getBounds()).thenReturn(new Rect(0, 0, 1000, 1000)); + when(windowInsets.getInsetsIgnoringVisibility(anyInt())).thenReturn(insets); + + // For the purposes of this test, just run everything synchronously + ShellExecutor shellExecutor = new TestShellExecutor(); + when(mTaskOrganizer.getExecutor()).thenReturn(shellExecutor); + } + + @After + public void tearDown() { + if (mController != null) { + mController.removeTask(); + mController = null; + } + } + + private void setUpTabletConfig() { + Configuration config = mock(Configuration.class); + config.smallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET; + mController.setConfig(config); + } + + private void setUpPhoneConfig() { + Configuration config = mock(Configuration.class); + config.smallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET - 1; + mController.setConfig(config); + } + + private void createController() { + mController = new FloatingTasksController(mContext, + mShellInit, + mShellController, + mock(ShellCommandHandler.class), + Optional.empty(), + mWindowManager, + mTaskOrganizer, + mock(TaskViewTransitions.class), + mock(ShellExecutor.class), + mock(ShellExecutor.class), + mock(SyncTransactionQueue.class)); + spyOn(mController); + } + + // + // Shell specific + // + @Test + public void instantiateController_addInitCallback() { + if (FLOATING_TASKS_ACTUALLY_ENABLED) { + createController(); + setUpTabletConfig(); + + verify(mShellInit, times(1)).addInitCallback(any(), any()); + } + } + + @Test + public void instantiateController_doesntAddInitCallback() { + if (!FLOATING_TASKS_ACTUALLY_ENABLED) { + createController(); + + verify(mShellInit, never()).addInitCallback(any(), any()); + } + } + + @Test + public void onInit_registerConfigChangeListener() { + if (FLOATING_TASKS_ACTUALLY_ENABLED) { + createController(); + setUpTabletConfig(); + mController.onInit(); + + verify(mShellController, times(1)).addConfigurationChangeListener(any()); + } + } + + // + // Tests for floating layer, which is only available for tablets. + // + + @Test + public void testIsFloatingLayerAvailable_true() { + createController(); + setUpTabletConfig(); + assertThat(mController.isFloatingLayerAvailable()).isTrue(); + } + + @Test + public void testIsFloatingLayerAvailable_false() { + createController(); + setUpPhoneConfig(); + assertThat(mController.isFloatingLayerAvailable()).isFalse(); + } + + // + // Tests for floating tasks being enabled, guarded by sysprop flag. + // + + @Test + public void testIsFloatingTasksEnabled_true() { + createController(); + mController.setFloatingTasksEnabled(true); + setUpTabletConfig(); + assertThat(mController.isFloatingTasksEnabled()).isTrue(); + } + + @Test + public void testIsFloatingTasksEnabled_false() { + createController(); + mController.setFloatingTasksEnabled(false); + setUpTabletConfig(); + assertThat(mController.isFloatingTasksEnabled()).isFalse(); + } + + // + // Tests for behavior depending on flags + // + + @Test + public void testShowTaskIntent_enabled() { + createController(); + mController.setFloatingTasksEnabled(true); + setUpTabletConfig(); + + mController.showTask(mock(Intent.class)); + verify(mWindowManager).addView(mFloatingTaskLayerCaptor.capture(), any()); + assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(1); + } + + @Test + public void testShowTaskIntent_notEnabled() { + createController(); + mController.setFloatingTasksEnabled(false); + setUpTabletConfig(); + + mController.showTask(mock(Intent.class)); + verify(mWindowManager, never()).addView(any(), any()); + } + + @Test + public void testRemoveTask() { + createController(); + mController.setFloatingTasksEnabled(true); + setUpTabletConfig(); + + mController.showTask(mock(Intent.class)); + verify(mWindowManager).addView(mFloatingTaskLayerCaptor.capture(), any()); + assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(1); + + mController.removeTask(); + verify(mWindowManager).removeView(mFloatingTaskLayerCaptor.capture()); + assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(0); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index a8d3bdcb7c96..1e08f1e55797 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -48,6 +48,7 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.onehanded.OneHandedController; +import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipAppOpsListener; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; @@ -85,6 +86,7 @@ public class PipControllerTest extends ShellTestCase { @Mock private ShellCommandHandler mMockShellCommandHandler; @Mock private DisplayController mMockDisplayController; @Mock private PhonePipMenuController mMockPhonePipMenuController; + @Mock private PipAnimationController mMockPipAnimationController; @Mock private PipAppOpsListener mMockPipAppOpsListener; @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm; @Mock private PhonePipKeepClearAlgorithm mMockPipKeepClearAlgorithm; @@ -117,8 +119,8 @@ public class PipControllerTest extends ShellTestCase { mShellController = spy(new ShellController(mShellInit, mMockShellCommandHandler, mMockExecutor)); mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler, - mShellController, mMockDisplayController, mMockPipAppOpsListener, - mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm, + mShellController, mMockDisplayController, mMockPipAnimationController, + mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm, mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper, @@ -183,8 +185,8 @@ public class PipControllerTest extends ShellTestCase { ShellInit shellInit = new ShellInit(mMockExecutor); assertNull(PipController.create(spyContext, shellInit, mMockShellCommandHandler, - mShellController, mMockDisplayController, mMockPipAppOpsListener, - mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm, + mShellController, mMockDisplayController, mMockPipAnimationController, + mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm, mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java index cadfeb0de312..b8aaaa76e3c7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java @@ -40,6 +40,7 @@ import static org.mockito.Mockito.when; import static java.lang.Integer.MAX_VALUE; import android.app.ActivityManager; +import android.app.ActivityTaskManager; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Rect; @@ -52,9 +53,8 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; -import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerImpl; -import com.android.wm.shell.desktopmode.DesktopMode; +import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellInit; @@ -68,7 +68,9 @@ import org.mockito.Mock; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Optional; +import java.util.function.Consumer; /** * Tests for {@link RecentTasksController}. @@ -85,11 +87,13 @@ public class RecentTasksControllerTest extends ShellTestCase { private ShellCommandHandler mShellCommandHandler; @Mock private DesktopModeTaskRepository mDesktopModeTaskRepository; + @Mock + private ActivityTaskManager mActivityTaskManager; private ShellTaskOrganizer mShellTaskOrganizer; private RecentTasksController mRecentTasksController; private ShellInit mShellInit; - private ShellExecutor mMainExecutor; + private TestShellExecutor mMainExecutor; @Before public void setUp() { @@ -97,8 +101,8 @@ public class RecentTasksControllerTest extends ShellTestCase { when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class)); mShellInit = spy(new ShellInit(mMainExecutor)); mRecentTasksController = spy(new RecentTasksController(mContext, mShellInit, - mShellCommandHandler, mTaskStackListener, Optional.of(mDesktopModeTaskRepository), - mMainExecutor)); + mShellCommandHandler, mTaskStackListener, mActivityTaskManager, + Optional.of(mDesktopModeTaskRepository), mMainExecutor)); mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler, null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController), mMainExecutor); @@ -188,10 +192,41 @@ public class RecentTasksControllerTest extends ShellTestCase { } @Test + public void testGetRecentTasks_ReturnsRecentTasksAsynchronously() { + @SuppressWarnings("unchecked") + final List<GroupedRecentTaskInfo>[] recentTasks = new List[1]; + Consumer<List<GroupedRecentTaskInfo>> consumer = argument -> recentTasks[0] = argument; + ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1); + ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2); + ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3); + ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4); + ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5); + ActivityManager.RecentTaskInfo t6 = makeTaskInfo(6); + setRawList(t1, t2, t3, t4, t5, t6); + + // Mark a couple pairs [t2, t4], [t3, t5] + SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4); + SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5); + + mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds); + mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds); + + mRecentTasksController.asRecentTasks() + .getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0, Runnable::run, consumer); + mMainExecutor.flushAll(); + + assertGroupedTasksListEquals(recentTasks[0], + t1.taskId, -1, + t2.taskId, t4.taskId, + t3.taskId, t5.taskId, + t6.taskId, -1); + } + + @Test public void testGetRecentTasks_groupActiveFreeformTasks() { StaticMockitoSession mockitoSession = mockitoSession().mockStatic( - DesktopMode.class).startMocking(); - when(DesktopMode.isActive(any())).thenReturn(true); + DesktopModeStatus.class).startMocking(); + when(DesktopModeStatus.isActive(any())).thenReturn(true); ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1); ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2); @@ -296,7 +331,7 @@ public class RecentTasksControllerTest extends ShellTestCase { for (ActivityManager.RecentTaskInfo task : tasks) { rawList.add(task); } - doReturn(rawList).when(mRecentTasksController).getRawRecentTasks(anyInt(), anyInt(), + doReturn(rawList).when(mActivityTaskManager).getRecentTasks(anyInt(), anyInt(), anyInt()); return rawList; } @@ -307,7 +342,7 @@ public class RecentTasksControllerTest extends ShellTestCase { * @param expectedTaskIds list of task ids that map to the flattened task ids of the tasks in * the grouped task list */ - private void assertGroupedTasksListEquals(ArrayList<GroupedRecentTaskInfo> recentTasks, + private void assertGroupedTasksListEquals(List<GroupedRecentTaskInfo> recentTasks, int... expectedTaskIds) { int[] flattenedTaskIds = new int[recentTasks.size() * 2]; for (int i = 0; i < recentTasks.size(); i++) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index 9240abfbe47f..835087007b30 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -16,7 +16,10 @@ package com.android.wm.shell.splitscreen; +import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED; +import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; @@ -28,11 +31,10 @@ import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -42,8 +44,10 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.content.res.Configuration; import android.graphics.Rect; +import android.os.Bundle; import android.view.SurfaceControl; import android.view.SurfaceSession; +import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -115,7 +119,6 @@ public class StageCoordinatorTests extends ShellTestCase { mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mMainExecutor, Optional.empty())); - doNothing().when(mStageCoordinator).updateActivityOptions(any(), anyInt()); when(mSplitLayout.getBounds1()).thenReturn(mBounds1); when(mSplitLayout.getBounds2()).thenReturn(mBounds2); @@ -303,4 +306,16 @@ public class StageCoordinatorTests extends ShellTestCase { verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any(), eq(false)); } + + @Test + public void testAddActivityOptions_addsBackgroundActivitiesFlags() { + Bundle options = mStageCoordinator.resolveStartStage(STAGE_TYPE_MAIN, + SPLIT_POSITION_UNDEFINED, null /* options */, null /* wct */); + + assertEquals(options.getParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, WindowContainerToken.class), + mMainStage.mRootTaskInfo.token); + assertTrue(options.getBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED)); + assertTrue(options.getBoolean( + KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION)); + } } diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING index 05fbc7a52ec6..23ee5055f5cc 100644 --- a/media/TEST_MAPPING +++ b/media/TEST_MAPPING @@ -1,6 +1,9 @@ { "presubmit": [ { + "name": "mediaroutertest" + }, + { "name": "CtsCameraTestCases", "options" : [ { diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index d8995b419f50..891ab45c7a6e 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -48,6 +48,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -118,6 +119,7 @@ public final class MediaRouter2 { private final Map<String, RoutingController> mNonSystemRoutingControllers = new ArrayMap<>(); private final AtomicInteger mNextRequestId = new AtomicInteger(1); + private final AtomicBoolean mIsScanning = new AtomicBoolean(/* initialValue= */ false); final Handler mHandler; @@ -234,7 +236,9 @@ public final class MediaRouter2 { @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) public void startScan() { if (isSystemRouter()) { - sManager.startScan(); + if (!mIsScanning.getAndSet(true)) { + sManager.registerScanRequest(); + } } } @@ -260,7 +264,9 @@ public final class MediaRouter2 { @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) public void stopScan() { if (isSystemRouter()) { - sManager.stopScan(); + if (mIsScanning.getAndSet(false)) { + sManager.unregisterScanRequest(); + } } } diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 071667a0e932..d79740c2fdff 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -79,9 +79,11 @@ public final class MediaRouter2Manager { final String mPackageName; private final Context mContext; - @GuardedBy("sLock") - private Client mClient; + + private final Client mClient; + private final IMediaRouterService mMediaRouterService; + private final AtomicInteger mScanRequestCount = new AtomicInteger(/* initialValue= */ 0); final Handler mHandler; final CopyOnWriteArrayList<CallbackRecord> mCallbackRecords = new CopyOnWriteArrayList<>(); @@ -119,7 +121,12 @@ public final class MediaRouter2Manager { .getSystemService(Context.MEDIA_SESSION_SERVICE); mPackageName = mContext.getPackageName(); mHandler = new Handler(context.getMainLooper()); - mHandler.post(this::getOrCreateClient); + mClient = new Client(); + try { + mMediaRouterService.registerManager(mClient, mPackageName); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } } /** @@ -155,48 +162,47 @@ public final class MediaRouter2Manager { } /** - * Starts scanning remote routes. - * <p> - * Route discovery can happen even when the {@link #startScan()} is not called. - * This is because the scanning could be started before by other apps. - * Therefore, calling this method after calling {@link #stopScan()} does not necessarily mean - * that the routes found before are removed and added again. - * <p> - * Use {@link Callback} to get the route related events. - * <p> - * @see #stopScan() + * Registers a request to scan for remote routes. + * + * <p>Increases the count of active scanning requests. When the count transitions from zero to + * one, sends a request to the system server to start scanning. + * + * <p>Clients must {@link #unregisterScanRequest() unregister their scan requests} when scanning + * is no longer needed, to avoid unnecessary resource usage. */ - public void startScan() { - Client client = getOrCreateClient(); - if (client != null) { + public void registerScanRequest() { + if (mScanRequestCount.getAndIncrement() == 0) { try { - mMediaRouterService.startScan(client); + mMediaRouterService.startScan(mClient); } catch (RemoteException ex) { - Log.e(TAG, "Unable to get sessions. Service probably died.", ex); + throw ex.rethrowFromSystemServer(); } } } /** - * Stops scanning remote routes to reduce resource consumption. - * <p> - * Route discovery can be continued even after this method is called. - * This is because the scanning is only turned off when all the apps stop scanning. - * Therefore, calling this method does not necessarily mean the routes are removed. - * Also, for the same reason it does not mean that {@link Callback#onRoutesAdded(List)} - * is not called afterwards. - * <p> - * Use {@link Callback} to get the route related events. + * Unregisters a scan request made by {@link #registerScanRequest()}. + * + * <p>Decreases the count of active scanning requests. When the count transitions from one to + * zero, sends a request to the system server to stop scanning. * - * @see #startScan() + * @throws IllegalStateException If called while there are no active scan requests. */ - public void stopScan() { - Client client = getOrCreateClient(); - if (client != null) { + public void unregisterScanRequest() { + if (mScanRequestCount.updateAndGet( + count -> { + if (count == 0) { + throw new IllegalStateException( + "No active scan requests to unregister."); + } else { + return --count; + } + }) + == 0) { try { - mMediaRouterService.stopScan(client); + mMediaRouterService.stopScan(mClient); } catch (RemoteException ex) { - Log.e(TAG, "Unable to get sessions. Service probably died.", ex); + throw ex.rethrowFromSystemServer(); } } } @@ -358,12 +364,10 @@ public final class MediaRouter2Manager { @Nullable public RoutingSessionInfo getSystemRoutingSession(@Nullable String packageName) { try { - return mMediaRouterService.getSystemSessionInfoForPackage( - getOrCreateClient(), packageName); + return mMediaRouterService.getSystemSessionInfoForPackage(mClient, packageName); } catch (RemoteException ex) { - Log.e(TAG, "Unable to get current system session info", ex); + throw ex.rethrowFromSystemServer(); } - return null; } /** @@ -424,15 +428,11 @@ public final class MediaRouter2Manager { */ @NonNull public List<RoutingSessionInfo> getRemoteSessions() { - Client client = getOrCreateClient(); - if (client != null) { - try { - return mMediaRouterService.getRemoteSessions(client); - } catch (RemoteException ex) { - Log.e(TAG, "Unable to get sessions. Service probably died.", ex); - } + try { + return mMediaRouterService.getRemoteSessions(mClient); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } - return Collections.emptyList(); } /** @@ -515,14 +515,11 @@ public final class MediaRouter2Manager { return; } - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.setRouteVolumeWithManager(client, requestId, route, volume); - } catch (RemoteException ex) { - Log.e(TAG, "Unable to set route volume.", ex); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.setRouteVolumeWithManager(mClient, requestId, route, volume); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -544,15 +541,12 @@ public final class MediaRouter2Manager { return; } - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.setSessionVolumeWithManager( - client, requestId, sessionInfo.getId(), volume); - } catch (RemoteException ex) { - Log.e(TAG, "Unable to set session volume.", ex); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.setSessionVolumeWithManager( + mClient, requestId, sessionInfo.getId(), volume); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -809,15 +803,12 @@ public final class MediaRouter2Manager { return; } - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.selectRouteWithManager( - client, requestId, sessionInfo.getId(), route); - } catch (RemoteException ex) { - Log.e(TAG, "selectRoute: Failed to send a request.", ex); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.selectRouteWithManager( + mClient, requestId, sessionInfo.getId(), route); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -851,15 +842,12 @@ public final class MediaRouter2Manager { return; } - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.deselectRouteWithManager( - client, requestId, sessionInfo.getId(), route); - } catch (RemoteException ex) { - Log.e(TAG, "deselectRoute: Failed to send a request.", ex); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.deselectRouteWithManager( + mClient, requestId, sessionInfo.getId(), route); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -876,15 +864,11 @@ public final class MediaRouter2Manager { public void releaseSession(@NonNull RoutingSessionInfo sessionInfo) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.releaseSessionWithManager( - client, requestId, sessionInfo.getId()); - } catch (RemoteException ex) { - Log.e(TAG, "releaseSession: Failed to send a request", ex); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.releaseSessionWithManager(mClient, requestId, sessionInfo.getId()); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -897,14 +881,11 @@ public final class MediaRouter2Manager { @NonNull MediaRoute2Info route) { int requestId = createTransferRequest(session, route); - Client client = getOrCreateClient(); - if (client != null) { - try { - mMediaRouterService.transferToRouteWithManager( - client, requestId, session.getId(), route); - } catch (RemoteException ex) { - Log.e(TAG, "transferToRoute: Failed to send a request.", ex); - } + try { + mMediaRouterService.transferToRouteWithManager( + mClient, requestId, session.getId(), route); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -917,14 +898,11 @@ public final class MediaRouter2Manager { int requestId = createTransferRequest(oldSession, route); - Client client = getOrCreateClient(); - if (client != null) { - try { - mMediaRouterService.requestCreateSessionWithManager( - client, requestId, oldSession, route); - } catch (RemoteException ex) { - Log.e(TAG, "requestCreateSession: Failed to send a request", ex); - } + try { + mMediaRouterService.requestCreateSessionWithManager( + mClient, requestId, oldSession, route); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -968,23 +946,6 @@ public final class MediaRouter2Manager { sessionInfo.getOwnerPackageName()); } - private Client getOrCreateClient() { - synchronized (sLock) { - if (mClient != null) { - return mClient; - } - Client client = new Client(); - try { - mMediaRouterService.registerManager(client, mPackageName); - mClient = client; - return client; - } catch (RemoteException ex) { - Log.e(TAG, "Unable to register media router manager.", ex); - } - } - return null; - } - /** * Interface for receiving events about media routing changes. */ diff --git a/media/jni/android_media_MediaProfiles.cpp b/media/jni/android_media_MediaProfiles.cpp index ecb1d510f807..9868a148d998 100644 --- a/media/jni/android_media_MediaProfiles.cpp +++ b/media/jni/android_media_MediaProfiles.cpp @@ -255,21 +255,21 @@ android_media_MediaProfiles_native_get_camcorder_profiles(JNIEnv *env, jobject / jmethodID audioProfileConstructorMethodID = env->GetMethodID(audioProfileClazz, "<init>", "(IIIII)V"); - jobjectArray videoCodecs = (jobjectArray)env->NewObjectArray( - cp->getVideoCodecs().size(), videoProfileClazz, nullptr); + jobjectArray videoCodecs = nullptr; { - int i = 0; + auto isAdvancedCodec = [](const MediaProfiles::VideoCodec *vc) -> bool { + return ((vc->getBitDepth() != 8 + || vc->getChromaSubsampling() != CHROMA_SUBSAMPLING_YUV_420 + || vc->getHdrFormat() != HDR_FORMAT_NONE)); + }; + std::vector<jobject> codecVector; for (const MediaProfiles::VideoCodec *vc : cp->getVideoCodecs()) { + if (isAdvancedCodec(vc) && !static_cast<bool>(advanced)) { + continue; + } chroma_subsampling cs = vc->getChromaSubsampling(); int bitDepth = vc->getBitDepth(); hdr_format hdr = vc->getHdrFormat(); - - bool isAdvanced = - (bitDepth != 8 || cs != CHROMA_SUBSAMPLING_YUV_420 || hdr != HDR_FORMAT_NONE); - if (static_cast<bool>(advanced) && !isAdvanced) { - continue; - } - jobject videoCodec = env->NewObject(videoProfileClazz, videoProfileConstructorMethodID, vc->getCodec(), @@ -281,10 +281,17 @@ android_media_MediaProfiles_native_get_camcorder_profiles(JNIEnv *env, jobject / static_cast<int>(cs), bitDepth, static_cast<int>(hdr)); - env->SetObjectArrayElement(videoCodecs, i++, videoCodec); + + codecVector.push_back(videoCodec); } - } + videoCodecs = (jobjectArray)env->NewObjectArray(codecVector.size(), + videoProfileClazz, nullptr); + int i = 0; + for (jobject codecObj : codecVector) { + env->SetObjectArrayElement(videoCodecs, i++, codecObj); + } + } jobjectArray audioCodecs; if (quality >= CAMCORDER_QUALITY_TIME_LAPSE_LIST_START && quality <= CAMCORDER_QUALITY_TIME_LAPSE_LIST_END) { diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp index 2da6c9884c0f..4f9c6f1ba68c 100644 --- a/media/tests/MediaRouter/Android.bp +++ b/media/tests/MediaRouter/Android.bp @@ -25,7 +25,7 @@ android_test { "testng", "truth-prebuilt", ], - + test_suites: ["general-tests"], platform_apis: true, certificate: "platform", } diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java index b4aad9df64a7..4086dec99218 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java @@ -39,6 +39,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import android.Manifest; @@ -121,7 +122,7 @@ public class MediaRouter2ManagerTest { MediaRouter2ManagerTestActivity.startActivity(mContext); mManager = MediaRouter2Manager.getInstance(mContext); - mManager.startScan(); + mManager.registerScanRequest(); mRouter2 = MediaRouter2.getInstance(mContext); // If we need to support thread pool executors, change this to thread pool executor. @@ -152,7 +153,7 @@ public class MediaRouter2ManagerTest { @After public void tearDown() { - mManager.stopScan(); + mManager.unregisterScanRequest(); // order matters (callbacks should be cleared at the last) releaseAllSessions(); @@ -818,6 +819,13 @@ public class MediaRouter2ManagerTest { assertFalse(failureLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); } + @Test + public void unregisterScanRequest_enforcesANonNegativeCount() { + mManager.unregisterScanRequest(); // One request was made in the test setup. + assertThrows(IllegalStateException.class, () -> mManager.unregisterScanRequest()); + mManager.registerScanRequest(); // So that the cleanup doesn't fail. + } + /** * Tests if getSelectableRoutes and getDeselectableRoutes filter routes based on * selected routes diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml index f448fbf7d51a..9cdd103e383f 100644 --- a/packages/CompanionDeviceManager/res/values-be/strings.xml +++ b/packages/CompanionDeviceManager/res/values-be/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Трансліруйце змесціва праграм з вашага тэлефона"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Дазвольце праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> мець доступ да гэтай інфармацыі з вашага тэлефона"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Сэрвісы для некалькіх прылад"</string> - <string name="helper_summary_app_streaming" msgid="5977509499890099">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на перадачу праграм плынню паміж вашымі прыладамі"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на трансляцыю праграм паміж вашымі прыладамі"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Дазвольце праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> мець доступ да гэтай інфармацыі з вашага тэлефона"</string> diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml index 7f3311470bf0..1c428aafe059 100644 --- a/packages/CompanionDeviceManager/res/values-bg/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Поточно предаване на приложенията на телефона ви"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Разрешете на <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да осъществява достъп до тази информация от телефона ви"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Услуги за различни устройства"</string> - <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> да предава поточно приложения между устройствата ви"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ иска разрешение от името на <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> да предава поточно приложения между устройствата ви"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Разрешете на <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да осъществява достъп до тази информация от телефона ви"</string> diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml index d62708ecb50d..2120515ee252 100644 --- a/packages/CompanionDeviceManager/res/values-de/strings.xml +++ b/packages/CompanionDeviceManager/res/values-de/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Smartphone-Apps streamen"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> Zugriff auf diese Informationen von deinem Smartphone gewähren"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Geräteübergreifende Dienste"</string> - <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet im Namen deines <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> um die Berechtigung zum Streamen von Apps zwischen deinen Geräten"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet für dein <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> um die Berechtigung zum Streamen von Apps zwischen deinen Geräten"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> Zugriff auf diese Informationen von deinem Smartphone gewähren"</string> diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml index c781ea166972..6d9ad9ddc778 100644 --- a/packages/CompanionDeviceManager/res/values-es/strings.xml +++ b/packages/CompanionDeviceManager/res/values-es/strings.xml @@ -23,12 +23,12 @@ <string name="summary_watch" msgid="3002344206574997652">"Se necesita esta aplicación para gestionar tu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> podrá interactuar con tus notificaciones y acceder a tus permisos de teléfono, SMS, contactos, calendario, registros de llamadas y dispositivos cercanos."</string> <string name="permission_apps" msgid="6142133265286656158">"Aplicaciones"</string> <string name="permission_apps_summary" msgid="798718816711515431">"Proyecta aplicaciones de tu teléfono"</string> - <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información desde tu teléfono"</string> + <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información de tu teléfono"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicios multidispositivo"</string> <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para emitir aplicaciones en otros dispositivos tuyos"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> - <string name="title_computer" msgid="4693714143506569253">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información desde tu teléfono"</string> + <string name="title_computer" msgid="4693714143506569253">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información de tu teléfono"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="permission_notification" msgid="693762568127741203">"Notificaciones"</string> <string name="permission_notification_summary" msgid="884075314530071011">"Puede leer todas las notificaciones, incluida información como contactos, mensajes y fotos"</string> diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml index ae9fdfb6b073..d5e9b0705364 100644 --- a/packages/CompanionDeviceManager/res/values-eu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml @@ -28,7 +28,7 @@ <string name="helper_summary_app_streaming" msgid="5977509499890099">"Gailu batetik bestera aplikazioak igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuaren izenean"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> - <string name="title_computer" msgid="4693714143506569253">"Eman informazio hori telefonotik hartzeko baimena <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari"</string> + <string name="title_computer" msgid="4693714143506569253">"Eman telefonoko informazio hau atzitzeko baimena <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="permission_notification" msgid="693762568127741203">"Jakinarazpenak"</string> <string name="permission_notification_summary" msgid="884075314530071011">"Jakinarazpen guztiak irakur ditzake; besteak beste, kontaktuak, mezuak, argazkiak eta antzeko informazioa"</string> diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml index 9a45788e745c..d1d02b4fc7b6 100644 --- a/packages/CompanionDeviceManager/res/values-fa/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml @@ -28,7 +28,7 @@ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> اجازه میخواهد ازطرف <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> برنامهها را بین دستگاههای شما جاریسازی کند"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> - <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> مجاز میشود به این اطلاعات در دستگاهتان دسترسی پیدا کند"</string> + <string name="title_computer" msgid="4693714143506569253">"به <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> اجازه دسترسی به این اطلاعات در دستگاهتان داده شود"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="permission_notification" msgid="693762568127741203">"اعلانها"</string> <string name="permission_notification_summary" msgid="884075314530071011">"میتواند همه اعلانها، ازجمله اطلاعاتی مثل مخاطبین، پیامها، و عکسها را بخواند"</string> @@ -40,7 +40,7 @@ <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"اجازه دادن"</string> <string name="consent_no" msgid="2640796915611404382">"اجازه ندادن"</string> - <string name="consent_back" msgid="2560683030046918882">"برگشت"</string> + <string name="consent_back" msgid="2560683030046918882">"برگشتن"</string> <string name="permission_sync_confirmation_title" msgid="667074294393493186">"انتقال اجازههای برنامه به ساعت"</string> <string name="permission_sync_summary" msgid="8873391306499120778">"برای آسانتر کردن راهاندازی ساعت، برنامههای نصبشده در ساعت درحین راهاندازی از همان اجازههای تلفن استفاده خواهند کرد.\n\n ممکن است این اجازهها شامل دسترسی به میکروفون و مکان ساعت باشد."</string> <string name="vendor_icon_description" msgid="4445875290032225965">"نماد برنامه"</string> diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml index eaf0d5460f64..aaaef4bc3c83 100644 --- a/packages/CompanionDeviceManager/res/values-gl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml @@ -28,7 +28,7 @@ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome do teu dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) para emitir contido de aplicacións entre os teus aparellos"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> - <string name="title_computer" msgid="4693714143506569253">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información desde o teu teléfono"</string> + <string name="title_computer" msgid="4693714143506569253">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información do teu teléfono"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="permission_notification" msgid="693762568127741203">"Notificacións"</string> <string name="permission_notification_summary" msgid="884075314530071011">"Pode ler todas as notificacións (que poden incluír información como contactos, mensaxes e fotos)"</string> diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml index c5ef913ecf2c..794aefe93682 100644 --- a/packages/CompanionDeviceManager/res/values-hi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml @@ -31,7 +31,7 @@ <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को अपने फ़ोन से यह जानकारी ऐक्सेस करने की अनुमति दें"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="permission_notification" msgid="693762568127741203">"सूचनाएं"</string> - <string name="permission_notification_summary" msgid="884075314530071011">"इससे सभी सूचनाएं देखी जा सकती हैं. इसमें संपर्क, मैसेज, और फ़ोटो जैसी जानकारी शामिल होती है"</string> + <string name="permission_notification_summary" msgid="884075314530071011">"इससे सभी सूचनाएं देखी जा सकती हैं. इनमें संपर्क, मैसेज, और फ़ोटो जैसी जानकारी शामिल होती है"</string> <string name="permission_storage" msgid="6831099350839392343">"फ़ोटो और मीडिया"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string> diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml index 3997deb9669d..6f2827507a1a 100644 --- a/packages/CompanionDeviceManager/res/values-is/strings.xml +++ b/packages/CompanionDeviceManager/res/values-is/strings.xml @@ -25,13 +25,13 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Streymdu forritum símans"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Veita <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aðgang að þessum upplýsingum úr símanum þínum"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Þjónustur á milli tækja"</string> - <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> sendir beiðni um heimild fyrir straumspilun forrita á milli tækjanna þinna fyrir hönd <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> sendir beiðni um heimild til straumspilunar forrita á milli tækjanna þinna fyrir hönd <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Veita <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aðgang að þessum upplýsingum úr símanum þínum"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="permission_notification" msgid="693762568127741203">"Tilkynningar"</string> - <string name="permission_notification_summary" msgid="884075314530071011">"Getur lesið allar tilkynningar, þar á meðal upplýsingar á borð við tengiliði skilaboð og myndir"</string> + <string name="permission_notification_summary" msgid="884075314530071011">"Getur lesið allar tilkynningar, þar á meðal upplýsingar á borð við tengiliði, skilaboð og myndir"</string> <string name="permission_storage" msgid="6831099350839392343">"Myndir og efni"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Þjónusta Google Play"</string> diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml index ab1cbf0f62b9..bcd4669aa9d8 100644 --- a/packages/CompanionDeviceManager/res/values-ja/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml @@ -28,7 +28,7 @@ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> が <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> に代わってデバイス間でアプリをストリーミングする権限をリクエストしています"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> - <string name="title_computer" msgid="4693714143506569253">"このスマートフォンからの情報へのアクセスを <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> に許可"</string> + <string name="title_computer" msgid="4693714143506569253">"スマートフォンのこの情報へのアクセスを <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> に許可"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="permission_notification" msgid="693762568127741203">"通知"</string> <string name="permission_notification_summary" msgid="884075314530071011">"連絡先、メッセージ、写真に関する情報を含め、すべての通知を読み取ることができます"</string> diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml index 525896b300a3..b7f8b5e2746a 100644 --- a/packages/CompanionDeviceManager/res/values-ko/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml @@ -28,7 +28,7 @@ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> 대신 기기 간에 앱을 스트리밍할 수 있는 권한을 요청하고 있습니다."</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> - <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 앱이 휴대전화에서 이 정보에 액세스하도록 허용합니다."</string> + <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 앱이 휴대전화에서 이 정보에 액세스하도록 허용"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="permission_notification" msgid="693762568127741203">"알림"</string> <string name="permission_notification_summary" msgid="884075314530071011">"연락처, 메시지, 사진 등의 정보를 포함한 모든 알림을 읽을 수 있습니다."</string> diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml index 24a309453999..3e7b0231796c 100644 --- a/packages/CompanionDeviceManager/res/values-ky/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml @@ -25,14 +25,14 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Телефондогу колдонмолорду алып ойнотуу"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна телефонуңуздагы ушул маалыматты көрүүгө уруксат бериңиз"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Түзмөктөр аралык кызматтар"</string> - <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздүн атынан түзмөктөрүңүздүн ортосунда колдонмолорду тышкы экранга чыгарууга уруксат сурап жатат"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздүн атынан түзмөктөрүңүздүн ортосунда колдонмолорду өткөрүүгө уруксат сурап жатат"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна телефонуңуздагы ушул маалыматты көрүүгө уруксат бериңиз"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="permission_notification" msgid="693762568127741203">"Билдирмелер"</string> <string name="permission_notification_summary" msgid="884075314530071011">"Бардык билдирмелерди, анын ичинде байланыштар, билдирүүлөр жана сүрөттөр сыяктуу маалыматты окуй алат"</string> - <string name="permission_storage" msgid="6831099350839392343">"Сүрөттөр жана медиа"</string> + <string name="permission_storage" msgid="6831099350839392343">"Сүрөттөр жана медиафайлдар"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play кызматтары"</string> <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздүн атынан телефондогу сүрөттөрдү, медиа файлдарды жана билдирмелерди колдонууга уруксат сурап жатат"</string> diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml index 6e59d5eed736..385474e092e4 100644 --- a/packages/CompanionDeviceManager/res/values-mk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml @@ -28,7 +28,7 @@ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на вашиот <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за да стримува апликации помеѓу вашите уреди"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> - <string name="title_computer" msgid="4693714143506569253">"Овозможете <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да пристапува до овие податоци на телефонот"</string> + <string name="title_computer" msgid="4693714143506569253">"Дозволете <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да пристапува до овие податоци на телефонот"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="permission_notification" msgid="693762568127741203">"Известувања"</string> <string name="permission_notification_summary" msgid="884075314530071011">"може да ги чита сите известувања, вклучително и податоци како контакти, пораки и фотографии"</string> diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml index 31b590357d1b..3ab18dbcad8f 100644 --- a/packages/CompanionDeviceManager/res/values-nl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml @@ -22,7 +22,7 @@ <string name="chooser_title" msgid="2262294130493605839">"Een <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiezen om te beheren met <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="summary_watch" msgid="3002344206574997652">"Deze app is vereist om je <xliff:g id="DEVICE_NAME">%1$s</xliff:g> te beheren. <xliff:g id="APP_NAME">%2$s</xliff:g> kan interactie hebben met je meldingen en toegang krijgen tot rechten voor Telefoon, Sms, Contacten, Agenda, Gesprekslijsten en Apparaten in de buurt."</string> <string name="permission_apps" msgid="6142133265286656158">"Apps"</string> - <string name="permission_apps_summary" msgid="798718816711515431">"De apps van je telefoon streamen"</string> + <string name="permission_apps_summary" msgid="798718816711515431">"Stream de apps van je telefoon"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toegang geven tot deze informatie op je telefoon"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device-services"</string> <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toestemming om apps te streamen tussen je apparaten"</string> diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml index 1ed65bd88d11..4eefbcd761b4 100644 --- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml @@ -28,10 +28,10 @@ <string name="helper_summary_app_streaming" msgid="5977509499890099">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para fazer stream de apps entre os seus dispositivos"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> - <string name="title_computer" msgid="4693714143506569253">"Permita que a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aceda a estas informações do seu telemóvel"</string> + <string name="title_computer" msgid="4693714143506569253">"Permita que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aceda a estas informações do seu telemóvel"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="permission_notification" msgid="693762568127741203">"Notificações"</string> - <string name="permission_notification_summary" msgid="884075314530071011">"Pode ler todas as notificações, incluindo informações como contratos, mensagens e fotos"</string> + <string name="permission_notification_summary" msgid="884075314530071011">"Pode ler todas as notificações, incluindo informações como contactos, mensagens e fotos"</string> <string name="permission_storage" msgid="6831099350839392343">"Fotos e multimédia"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Serviços do Google Play"</string> diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml index eb041320a173..d982d5f743b7 100644 --- a/packages/CompanionDeviceManager/res/values-ro/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml @@ -17,18 +17,18 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Manager de dispozitiv Companion"</string> - <string name="confirmation_title" msgid="3785000297483688997">"Permiteți ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să vă acceseze dispozitivul <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="3785000297483688997">"Permite ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze dispozitivul <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_watch" msgid="576290739483672360">"ceas"</string> <string name="chooser_title" msgid="2262294130493605839">"Alege un profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> pe care să îl gestioneze <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="3002344206574997652">"Această aplicație este necesară pentru a gestiona <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> va putea să interacționeze cu notificările dvs. și să vă acceseze permisiunile pentru Telefon, SMS, Agendă, Calendar, Jurnale de apeluri și Dispozitive din apropiere."</string> + <string name="summary_watch" msgid="3002344206574997652">"Această aplicație este necesară pentru a gestiona <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> va putea să interacționeze cu notificările și să acceseze permisiunile pentru Telefon, SMS, Agendă, Calendar, Jurnale de apeluri și Dispozitive din apropiere."</string> <string name="permission_apps" msgid="6142133265286656158">"Aplicații"</string> <string name="permission_apps_summary" msgid="798718816711515431">"Să redea în stream aplicațiile telefonului"</string> - <string name="title_app_streaming" msgid="2270331024626446950">"Permiteți ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze aceste informații de pe telefon"</string> + <string name="title_app_streaming" msgid="2270331024626446950">"Permite ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze aceste informații de pe telefon"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicii pe mai multe dispozitive"</string> - <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a reda în stream aplicații între dispozitivele dvs."</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a reda în stream aplicații între dispozitivele tale"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> - <string name="title_computer" msgid="4693714143506569253">"Permiteți ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze aceste informații de pe telefon"</string> + <string name="title_computer" msgid="4693714143506569253">"Permite ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze aceste informații de pe telefon"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="permission_notification" msgid="693762568127741203">"Notificări"</string> <string name="permission_notification_summary" msgid="884075314530071011">"Poate să citească toate notificările, inclusiv informații cum ar fi agenda, mesajele și fotografiile"</string> @@ -38,11 +38,11 @@ <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a accesa fotografiile, conținutul media și notificările de pe telefon"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string> <string name="summary_generic" msgid="2346762210105903720"></string> - <string name="consent_yes" msgid="8344487259618762872">"Permiteți"</string> - <string name="consent_no" msgid="2640796915611404382">"Nu permiteți"</string> + <string name="consent_yes" msgid="8344487259618762872">"Permite"</string> + <string name="consent_no" msgid="2640796915611404382">"Nu permite"</string> <string name="consent_back" msgid="2560683030046918882">"Înapoi"</string> - <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transferați permisiunile pentru aplicații pe ceas"</string> - <string name="permission_sync_summary" msgid="8873391306499120778">"Ca să configurați mai ușor ceasul, aplicațiile instalate pe ceas în timpul procesului de configurare vor folosi aceleași permisiuni ca telefonul.\n\n Între acestea se poate număra accesul la microfonul și locația ceasului."</string> + <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transferă permisiunile pentru aplicații pe ceas"</string> + <string name="permission_sync_summary" msgid="8873391306499120778">"Ca să configurezi mai ușor ceasul, aplicațiile instalate pe ceas în timpul procesului de configurare vor folosi aceleași permisiuni ca telefonul.\n\n Între acestea se poate număra accesul la microfonul și locația ceasului."</string> <string name="vendor_icon_description" msgid="4445875290032225965">"Pictograma aplicației"</string> <string name="vendor_header_button_description" msgid="6566660389500630608">"Butonul Mai multe informații"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml index fa7155e2b93a..a7d8c5e2dc7f 100644 --- a/packages/CompanionDeviceManager/res/values-sk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Streamovať aplikácie telefónu"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Povoľte aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> prístup k týmto informáciám z vášho telefónu"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Služby pre viacero zariadení"</string> - <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje povolenie na streamovanie aplikácií medzi vašimi zariadeniami v mene tohto zariadenia (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje povolenie na streamovanie aplikácií medzi vašimi zariadeniami v mene tohto zariadenia (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Povoľte aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> prístup k týmto informáciám z vášho telefónu"</string> diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml index 24465fca55cf..966ff55688e8 100644 --- a/packages/CompanionDeviceManager/res/values-sw/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Tiririsha programu za simu yako"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Ruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ifikie maelezo haya kutoka kwenye simu yako"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Huduma za kifaa kilichounganishwa kwingine"</string> - <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili itiririshe programu kati ya vifaa vyako"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili itiririshe programu kati ya vifaa vyako"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Ruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ifikie maelezo haya kutoka kwenye simu yako"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Picha na maudhui"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Huduma za Google Play"</string> - <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili ifikie picha, maudhui na arifa za simu yako"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili ifikie picha, maudhui na arifa za simu yako"</string> <string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string> diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml index 744007904512..cd960958e43b 100644 --- a/packages/CompanionDeviceManager/res/values-te/strings.xml +++ b/packages/CompanionDeviceManager/res/values-te/strings.xml @@ -31,7 +31,7 @@ <string name="title_computer" msgid="4693714143506569253">"మీ ఫోన్ నుండి ఈ సమాచారాన్ని యాక్సెస్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> యాప్ను అనుమతించండి"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="permission_notification" msgid="693762568127741203">"నోటిఫికేషన్లు"</string> - <string name="permission_notification_summary" msgid="884075314530071011">"కాంటాక్ట్లు, మెసేజ్లు, ఫోటోల వంటి సమాచారంతో సహా అన్ని నోటిఫికేషన్లను చదవగలరు"</string> + <string name="permission_notification_summary" msgid="884075314530071011">"కాంటాక్ట్లు, మెసేజ్లు, ఫోటోల వంటి సమాచారంతో సహా అన్ని నోటిఫికేషన్లను చదవగలదు"</string> <string name="permission_storage" msgid="6831099350839392343">"ఫోటోలు, మీడియా"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play సర్వీసులు"</string> diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml index 54282106fc4c..5212453641ea 100644 --- a/packages/CompanionDeviceManager/res/values-ur/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml @@ -23,12 +23,12 @@ <string name="summary_watch" msgid="3002344206574997652">"آپ کے <xliff:g id="DEVICE_NAME">%1$s</xliff:g> کا نظم کرنے کے لئے اس ایپ کی ضرورت ہے۔ <xliff:g id="APP_NAME">%2$s</xliff:g> کو آپ کی اطلاعات کے ساتھ تعامل کرنے اور آپ کے فون، SMS، رابطوں، کیلنڈر، کال لاگز اور قریبی آلات کی اجازتوں تک رسائی کی اجازت ہوگی۔"</string> <string name="permission_apps" msgid="6142133265286656158">"ایپس"</string> <string name="permission_apps_summary" msgid="798718816711515431">"اپنے فون کی ایپس کی سلسلہ بندی کریں"</string> - <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو اپنے فون سے ان معلومات تک رسائی حاصل کرنے کی اجازت دیں"</string> + <string name="title_app_streaming" msgid="2270331024626446950">"اپنے فون سے ان معلومات تک رسائی حاصل کرنے کی <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو اجازت دیں"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"کراس ڈیوائس سروسز"</string> <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> کی جانب سے آپ کے آلات کے درمیان ایپس کی سلسلہ بندی کرنے کی اجازت کی درخواست کر رہی ہے"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> - <string name="title_computer" msgid="4693714143506569253">"اپنے فون سے اس معلومات تک رسائی حاصل Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کرنے کی اجازت دیں"</string> + <string name="title_computer" msgid="4693714143506569253">"اپنے فون سے اس معلومات تک رسائی حاصل کرنے کی <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو اجازت دیں"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="permission_notification" msgid="693762568127741203">"اطلاعات"</string> <string name="permission_notification_summary" msgid="884075314530071011">"رابطوں، پیغامات اور تصاویر جیسی معلومات سمیت تمام اطلاعات پڑھ سکتے ہیں"</string> diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml index d574cec771a7..8f58d5471c91 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"串流播放手機應用程式內容"</string> <string name="title_app_streaming" msgid="2270331024626446950">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>存取您手機中的這項資料"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"跨裝置服務"</string> - <string name="helper_summary_app_streaming" msgid="5977509499890099">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> 要求權限,以便在裝置之間串流應用程式內容"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在為 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> 要求權限,以在裝置之間串流應用程式內容"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>存取您手機中的這項資料"</string> diff --git a/packages/DynamicSystemInstallationService/res/values-ro/strings.xml b/packages/DynamicSystemInstallationService/res/values-ro/strings.xml index 22a46d5ec748..a8a512569de3 100644 --- a/packages/DynamicSystemInstallationService/res/values-ro/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-ro/strings.xml @@ -1,14 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="keyguard_description" msgid="8582605799129954556">"Introdu parola și accesați Actualizările de sistem dinamice"</string> - <string name="notification_install_completed" msgid="6252047868415172643">"Sistemul dinamic este pregătit. Ca să începeți să-l folosiți, reporniți dispozitivul."</string> + <string name="keyguard_description" msgid="8582605799129954556">"Introdu parola și accesează Actualizările de sistem dinamice"</string> + <string name="notification_install_completed" msgid="6252047868415172643">"Sistemul dinamic e pregătit. Ca să începi să-l folosești, repornește dispozitivul."</string> <string name="notification_install_inprogress" msgid="7383334330065065017">"Se instalează"</string> <string name="notification_install_failed" msgid="4066039210317521404">"Instalarea nu a reușit"</string> - <string name="notification_image_validation_failed" msgid="2720357826403917016">"Nu s-a validat imaginea. Abandonați instalarea."</string> + <string name="notification_image_validation_failed" msgid="2720357826403917016">"Nu s-a validat imaginea. Abandonează instalarea."</string> <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Rulăm un sistem dinamic. Repornește pentru a folosi versiunea Android inițială."</string> <string name="notification_action_cancel" msgid="5929299408545961077">"Anulează"</string> - <string name="notification_action_discard" msgid="1817481003134947493">"Renunțați"</string> + <string name="notification_action_discard" msgid="1817481003134947493">"Renunță"</string> <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Repornește"</string> <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Repornește"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"S-a renunțat la sistemul dinamic"</string> diff --git a/packages/PackageInstaller/res/values-ro/strings.xml b/packages/PackageInstaller/res/values-ro/strings.xml index 1f156190b5fd..9ca454377cd0 100644 --- a/packages/PackageInstaller/res/values-ro/strings.xml +++ b/packages/PackageInstaller/res/values-ro/strings.xml @@ -24,41 +24,41 @@ <string name="installing" msgid="4921993079741206516">"Se instalează…"</string> <string name="installing_app" msgid="1165095864863849422">"Se instalează <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string> <string name="install_done" msgid="5987363587661783896">"Aplicație instalată."</string> - <string name="install_confirm_question" msgid="7663733664476363311">"Doriți să instalați această aplicație?"</string> - <string name="install_confirm_question_update" msgid="3348888852318388584">"Doriți să actualizați această aplicație?"</string> + <string name="install_confirm_question" msgid="7663733664476363311">"Vrei să instalezi această aplicație?"</string> + <string name="install_confirm_question_update" msgid="3348888852318388584">"Vrei să actualizezi această aplicație?"</string> <string name="install_failed" msgid="5777824004474125469">"Aplicația nu a fost instalată."</string> <string name="install_failed_blocked" msgid="8512284352994752094">"Instalarea pachetului a fost blocată."</string> <string name="install_failed_conflict" msgid="3493184212162521426">"Aplicația nu a fost instalată deoarece pachetul intră în conflict cu un pachet existent."</string> - <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"Aplicația nu a fost instalată deoarece nu este compatibilă cu tableta dvs."</string> - <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"Aplicația nu este compatibilă cu televizorul dvs."</string> - <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"Aplicația nu a fost instalată deoarece nu este compatibilă cu telefonul dvs."</string> + <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"Aplicația nu a fost instalată deoarece nu este compatibilă cu tableta."</string> + <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"Aplicația nu este compatibilă cu televizorul."</string> + <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"Aplicația nu a fost instalată deoarece nu este compatibilă cu telefonul."</string> <string name="install_failed_invalid_apk" msgid="8581007676422623930">"Aplicația nu a fost instalată deoarece pachetul este nevalid."</string> - <string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe tableta dvs."</string> - <string name="install_failed_msg" product="tv" msgid="1920009940048975221">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe televizorul dvs."</string> - <string name="install_failed_msg" product="default" msgid="6484461562647915707">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe telefonul dvs."</string> + <string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe tabletă."</string> + <string name="install_failed_msg" product="tv" msgid="1920009940048975221">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe televizor."</string> + <string name="install_failed_msg" product="default" msgid="6484461562647915707">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe telefon."</string> <string name="launch" msgid="3952550563999890101">"Deschide"</string> <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"Administratorul nu permite instalarea aplicațiilor obținute din surse necunoscute"</string> <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Aplicațiile necunoscute nu pot fi instalate de acest utilizator"</string> <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Acest utilizator nu are permisiunea să instaleze aplicații"</string> <string name="ok" msgid="7871959885003339302">"OK"</string> - <string name="manage_applications" msgid="5400164782453975580">"Gestionați aplicații"</string> + <string name="manage_applications" msgid="5400164782453975580">"Gestionează"</string> <string name="out_of_space_dlg_title" msgid="4156690013884649502">"Spațiu de stocare insuficient"</string> - <string name="out_of_space_dlg_text" msgid="8727714096031856231">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată. Eliberați spațiu și încercați din nou."</string> + <string name="out_of_space_dlg_text" msgid="8727714096031856231">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată. Eliberează spațiu și încearcă din nou."</string> <string name="app_not_found_dlg_title" msgid="5107924008597470285">"Aplicația nu a fost găsită"</string> <string name="app_not_found_dlg_text" msgid="5219983779377811611">"Aplicația nu a fost găsită în lista de aplicații instalate."</string> <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"Nepermis"</string> <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"Utilizatorul actual nu are permisiune pentru a face această dezinstalare."</string> <string name="generic_error_dlg_title" msgid="5863195085927067752">"Eroare"</string> <string name="generic_error_dlg_text" msgid="5287861443265795232">"Aplicația nu a putut fi dezinstalată."</string> - <string name="uninstall_application_title" msgid="4045420072401428123">"Dezinstalați aplicația"</string> - <string name="uninstall_update_title" msgid="824411791011583031">"Dezinstalați actualizarea"</string> + <string name="uninstall_application_title" msgid="4045420072401428123">"Dezinstalează aplicația"</string> + <string name="uninstall_update_title" msgid="824411791011583031">"Dezinstalează actualizarea"</string> <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> face parte din următoarea aplicație:"</string> - <string name="uninstall_application_text" msgid="3816830743706143980">"Doriți să dezinstalați această aplicație?"</string> - <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Doriți să dezinstalați această aplicație pentru "<b>"toți"</b>" utilizatorii? Aplicația și datele acesteia vor fi eliminate de la "<b>"toți"</b>" utilizatorii de pe acest dispozitiv."</string> - <string name="uninstall_application_text_user" msgid="498072714173920526">"Dezinstalați această aplicație pentru utilizatorul <xliff:g id="USERNAME">%1$s</xliff:g>?"</string> - <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Doriți să dezinstalați această aplicație din profilul de serviciu?"</string> - <string name="uninstall_update_text" msgid="863648314632448705">"Înlocuiți această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate."</string> - <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Înlocuiți această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate. Această acțiune va afecta toți utilizatorii dispozitivului, inclusiv pe cei cu profiluri de serviciu."</string> + <string name="uninstall_application_text" msgid="3816830743706143980">"Dezinstalezi această aplicație?"</string> + <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Dezinstalezi această aplicație pentru "<b>"toți"</b>" utilizatorii? Aplicația și datele acesteia vor fi eliminate de la "<b>"toți"</b>" utilizatorii de pe acest dispozitiv."</string> + <string name="uninstall_application_text_user" msgid="498072714173920526">"Dezinstalezi această aplicație pentru utilizatorul <xliff:g id="USERNAME">%1$s</xliff:g>?"</string> + <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Dezinstalezi această aplicație din profilul de serviciu?"</string> + <string name="uninstall_update_text" msgid="863648314632448705">"Înlocuiești această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate."</string> + <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Înlocuiești această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate. Această acțiune va afecta toți utilizatorii dispozitivului, inclusiv pe cei cu profiluri de serviciu."</string> <string name="uninstall_keep_data" msgid="7002379587465487550">"Păstrează <xliff:g id="SIZE">%1$s</xliff:g> din datele aplicației."</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Dezinstalări în curs"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Dezinstalări nereușite"</string> @@ -71,23 +71,23 @@ <string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"Nu se poate dezinstala aplicația activă de administrare a dispozitivului"</string> <string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"Nu se poate dezinstala aplicația activă de administrare a dispozitivului pentru <xliff:g id="USERNAME">%1$s</xliff:g>"</string> <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"Aplicația este necesară unor utilizatori sau profiluri și a fost dezinstalată pentru alții"</string> - <string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"Aplicația este necesară pentru profilul dvs. și nu poate fi dezinstalată."</string> + <string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"Aplicația este necesară pentru profilul tău și nu poate fi dezinstalată."</string> <string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"Aplicația este necesară administratorului dispozitivului și nu poate fi dezinstalată."</string> - <string name="manage_device_administrators" msgid="3092696419363842816">"Gestionați aplicațiile de administrare dispozitiv"</string> - <string name="manage_users" msgid="1243995386982560813">"Gestionați utilizatorii"</string> + <string name="manage_device_administrators" msgid="3092696419363842816">"Gestionează aplicațiile de administrare dispozitiv"</string> + <string name="manage_users" msgid="1243995386982560813">"Gestionează utilizatorii"</string> <string name="uninstall_failed_msg" msgid="2176744834786696012">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi dezinstalată."</string> <string name="Parse_error_dlg_text" msgid="1661404001063076789">"A apărut o problemă la analizarea pachetului."</string> <string name="wear_not_allowed_dlg_title" msgid="8664785993465117517">"Android Wear"</string> <string name="wear_not_allowed_dlg_text" msgid="704615521550939237">"Acțiunile de instalare și dezinstalare nu sunt acceptate pe Wear."</string> <string name="message_staging" msgid="8032722385658438567">"Se pregătește aplicația…"</string> <string name="app_name_unknown" msgid="6881210203354323926">"Necunoscut"</string> - <string name="untrusted_external_source_warning" product="tablet" msgid="7067510047443133095">"Din motive de securitate, tableta dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă. Puteți modifica această opțiune în Setări."</string> - <string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"Din motive de securitate, televizorul dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă. Puteți modifica această opțiune în Setări."</string> - <string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"Din motive de securitate, telefonul dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă. Puteți modifica această opțiune în Setări."</string> - <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonul și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați această aplicație, acceptați că sunteți singura persoană responsabilă pentru deteriorarea telefonului sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string> - <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tableta și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați aplicația, acceptați că sunteți singura persoană responsabilă pentru deteriorarea tabletei sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string> - <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Televizorul și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați această aplicație, acceptați că sunteți singura persoană responsabilă pentru deteriorarea televizorului sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string> - <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuați"</string> + <string name="untrusted_external_source_warning" product="tablet" msgid="7067510047443133095">"Din motive de securitate, tableta nu are permisiunea să instaleze aplicații necunoscute din această sursă. Poți modifica această opțiune în setări."</string> + <string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"Din motive de securitate, televizorul nu are permisiunea să instaleze aplicații necunoscute din această sursă. Poți modifica această opțiune în setări."</string> + <string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"Din motive de securitate, telefonul nu are permisiunea să instaleze aplicații necunoscute din această sursă. Poți modifica această opțiune în setări."</string> + <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonul și datele tale personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalezi această aplicație, accepți că ești singura persoană responsabilă pentru deteriorarea telefonului sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string> + <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tableta și datele tale personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalezi aplicația, accepți că ești singura persoană responsabilă pentru deteriorarea tabletei sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string> + <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Televizorul și datele tale cu caracter personal sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalezi această aplicație, accepți că ești singura persoană responsabilă pentru deteriorarea televizorului sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string> + <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuă"</string> <string name="external_sources_settings" msgid="4046964413071713807">"Setări"</string> <string name="wear_app_channel" msgid="1960809674709107850">"Se (dez)instalează aplicațiile Wear"</string> <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notificare de aplicație instalată"</string> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml index 18b6a0e4c830..ff260f528c99 100644 --- a/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Respingeți"</string> + <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Închide"</string> </resources> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 4c3563302dd8..46cba252e5e1 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -641,7 +641,7 @@ <string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string> <string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string> <string name="data_connection_carrier_wifi" msgid="8932949159370130465">"W+"</string> - <string name="cell_data_off_content_description" msgid="2280700839891636498">"Мобильдік деректер өшірулі"</string> + <string name="cell_data_off_content_description" msgid="2280700839891636498">"Мобильдік интернет өшірулі"</string> <string name="not_default_data_content_description" msgid="6517068332106592887">"Деректерді пайдалануға реттелмеген."</string> <string name="accessibility_no_phone" msgid="2687419663127582503">"Телефон жоқ."</string> <string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Телефон бір баған."</string> diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml index 987b9c36caac..faa15c253fb4 100644 --- a/packages/SettingsLib/res/values-ro/arrays.xml +++ b/packages/SettingsLib/res/values-ro/arrays.xml @@ -113,7 +113,7 @@ <item msgid="8887519571067543785">"96,0 kHz"</item> </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_summaries"> - <item msgid="2284090879080331090">"Folosiți selectarea sistemului (prestabilit)"</item> + <item msgid="2284090879080331090">"Folosește selectarea sistemului (prestabilit)"</item> <item msgid="1872276250541651186">"44,1 kHz"</item> <item msgid="8736780630001704004">"48,0 kHz"</item> <item msgid="7698585706868856888">"88,2 kHz"</item> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 2ce7ccff8dde..b3c701827512 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -143,8 +143,8 @@ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="7324694226276491807">"Folosește pentru profilul pentru conținut media audio"</string> <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Folosește pentru componenta audio a telefonului"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Folosește pentru transferul de fișiere"</string> - <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utilizați pentru introducere date"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Folosiți pentru aparatele auditive"</string> + <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Folosește pentru introducere date"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Folosește pentru aparatele auditive"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Folosește pentru LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Asociază"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"CONECTEAZĂ"</string> @@ -206,7 +206,7 @@ <string name="tts_status_not_supported" msgid="2702997696245523743">"<xliff:g id="LOCALE">%1$s</xliff:g> nu este acceptată"</string> <string name="tts_status_checking" msgid="8026559918948285013">"Se verifică…"</string> <string name="tts_engine_settings_title" msgid="7849477533103566291">"Setări pentru <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string> - <string name="tts_engine_settings_button" msgid="477155276199968948">"Lansați setările motorului"</string> + <string name="tts_engine_settings_button" msgid="477155276199968948">"Lansează setările motorului"</string> <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Motor preferat"</string> <string name="tts_general_section_title" msgid="8919671529502364567">"Preferințe generale"</string> <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Resetează tonalitatea vorbirii"</string> @@ -289,11 +289,11 @@ <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versiunea AVRCP pentru Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selectează versiunea AVRCP pentru Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versiunea MAP pentru Bluetooth"</string> - <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Selectați versiunea MAP pentru Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Selectează versiunea MAP pentru Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec audio Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Declanșează codecul audio Bluetooth\nSelecție"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Rată de eșantionare audio Bluetooth"</string> - <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5876305103137067798">"Declanșați codecul audio Bluetooth\nSelecție: rată de eșantionare"</string> + <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5876305103137067798">"Declanșează codecul audio Bluetooth\nSelecție: rată de eșantionare"</string> <string name="bluetooth_select_a2dp_codec_type_help_info" msgid="8647200416514412338">"O opțiune inactivă înseamnă incompatibilitate cu telefonul sau setul căști-microfon"</string> <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="6253965294594390806">"Biți audio Bluetooth per eșantion"</string> <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"Declanșează codecul audio Bluetooth\nSelecție: biți per eșantion"</string> @@ -309,7 +309,7 @@ <string name="private_dns_mode_provider" msgid="3619040641762557028">"Nume de gazdă al furnizorului de DNS privat"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"Introdu numele de gazdă al furnizorului de DNS"</string> <string name="private_dns_mode_provider_failure" msgid="8356259467861515108">"Nu s-a putut conecta"</string> - <string name="wifi_display_certification_summary" msgid="8111151348106907513">"Afișați opțiunile pentru certificarea Ecran wireless"</string> + <string name="wifi_display_certification_summary" msgid="8111151348106907513">"Afișează opțiunile pentru certificarea Ecran wireless"</string> <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Mărește nivelul de înregistrare prin Wi‑Fi, afișează după SSID RSSI în Selectorul Wi‑Fi"</string> <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduce descărcarea bateriei și îmbunătățește performanța rețelei"</string> <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Când acest mod este activat, adresa MAC a dispozitivului se poate schimba de fiecare dată când se conectează la o rețea care are activată randomizarea MAC."</string> @@ -326,16 +326,16 @@ <string name="allow_mock_location" msgid="2102650981552527884">"Permite locațiile fictive"</string> <string name="allow_mock_location_summary" msgid="179780881081354579">"Permite locațiile fictive"</string> <string name="debug_view_attributes" msgid="3539609843984208216">"Activează inspectarea atributelor de vizualizare"</string> - <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Păstrați întotdeauna conexiunea de date mobile activată, chiar și atunci când funcția Wi‑Fi este activată (pentru comutarea rapidă între rețele)."</string> + <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Păstrează întotdeauna conexiunea de date mobile activată, chiar și atunci când funcția Wi‑Fi este activată (pentru comutarea rapidă între rețele)."</string> <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Folosește accelerarea hardware pentru tethering, dacă este disponibilă"</string> - <string name="adb_warning_title" msgid="7708653449506485728">"Permiteți remedierea erorilor prin USB?"</string> - <string name="adb_warning_message" msgid="8145270656419669221">"Remedierea erorilor prin USB are exclusiv scopuri de dezvoltare. Utilizați-o pentru a copia date de pe computer pe dispozitiv, pentru a instala aplicații pe dispozitiv fără notificare și pentru a citi datele din jurnale."</string> + <string name="adb_warning_title" msgid="7708653449506485728">"Permiți remedierea erorilor prin USB?"</string> + <string name="adb_warning_message" msgid="8145270656419669221">"Remedierea erorilor prin USB are exclusiv scopuri de dezvoltare. Folosește-o pentru a copia date de pe computer pe dispozitiv, pentru a instala aplicații pe dispozitiv fără notificare și pentru a citi datele din jurnale."</string> <string name="adbwifi_warning_title" msgid="727104571653031865">"Permiți remedierea erorilor wireless?"</string> <string name="adbwifi_warning_message" msgid="8005936574322702388">"Remedierea erorilor wireless are exclusiv scopuri de dezvoltare. Folosește-o pentru a copia date de pe computer pe dispozitiv, pentru a instala aplicații pe dispozitiv fără notificare și pentru a citi datele din jurnale."</string> <string name="adb_keys_warning_message" msgid="2968555274488101220">"Revoci accesul la remedierea erorilor prin USB de pe toate computerele pe care le-ai autorizat anterior?"</string> <string name="dev_settings_warning_title" msgid="8251234890169074553">"Permiți setările pentru dezvoltare?"</string> <string name="dev_settings_warning_message" msgid="37741686486073668">"Aceste setări sunt destinate exclusiv utilizării pentru dezvoltare. Din cauza lor, este posibil ca dispozitivul tău și aplicațiile de pe acesta să nu mai funcționeze sau să funcționeze necorespunzător."</string> - <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Verificați aplicațiile prin USB"</string> + <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Verifică aplicațiile prin USB"</string> <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Verifică aplicațiile instalate utilizând ADB/ADT, pentru a detecta un comportament dăunător."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Vor fi afișate dispozitivele Bluetooth fără nume (numai adresele MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Dezactivează funcția Bluetooth de volum absolut în cazul problemelor de volum apărute la dispozitivele la distanță, cum ar fi volumul mult prea ridicat sau lipsa de control asupra acestuia."</string> @@ -393,7 +393,7 @@ <string name="window_animation_scale_title" msgid="5236381298376812508">"Scară animație fereastră"</string> <string name="transition_animation_scale_title" msgid="1278477690695439337">"Scară tranziție animații"</string> <string name="animator_duration_scale_title" msgid="7082913931326085176">"Scară durată Animator"</string> - <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simulați afișaje secundare"</string> + <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simulează afișaje secundare"</string> <string name="debug_applications_category" msgid="5394089406638954196">"Aplicații"</string> <string name="immediately_destroy_activities" msgid="1826287490705167403">"Nu păstra activitățile"</string> <string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"Elimină activitățile imediat ce utilizatorul le închide"</string> @@ -404,10 +404,10 @@ <string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"Afișează avertisment pe ecran când o aplicație postează o notificare fără canal valid"</string> <string name="force_allow_on_external" msgid="9187902444231637880">"Forțează accesul aplicațiilor la stocarea externă"</string> <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Permite scrierea oricărei aplicații eligibile în stocarea externă, indiferent de valorile manifestului"</string> - <string name="force_resizable_activities" msgid="7143612144399959606">"Forțați redimensionarea activităților"</string> - <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Permiteți redimensionarea tuturor activităților pentru modul cu ferestre multiple, indiferent de valorile manifestului."</string> + <string name="force_resizable_activities" msgid="7143612144399959606">"Forțează redimensionarea activităților"</string> + <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Permite redimensionarea tuturor activităților pentru modul cu ferestre multiple, indiferent de valorile manifestului."</string> <string name="enable_freeform_support" msgid="7599125687603914253">"Activează ferestrele cu formă liberă"</string> - <string name="enable_freeform_support_summary" msgid="1822862728719276331">"Activați compatibilitatea pentru ferestrele experimentale cu formă liberă."</string> + <string name="enable_freeform_support_summary" msgid="1822862728719276331">"Activează compatibilitatea pentru ferestrele experimentale cu formă liberă."</string> <string name="desktop_mode" msgid="2389067840550544462">"Modul desktop"</string> <string name="local_backup_password_title" msgid="4631017948933578709">"Parolă backup computer"</string> <string name="local_backup_password_summary_none" msgid="7646898032616361714">"În prezent, backupurile complete pe computer nu sunt protejate"</string> @@ -449,7 +449,7 @@ <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (roșu-verde)"</string> <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (albastru-galben)"</string> <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corecția culorii"</string> - <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Corecția culorii poate fi utilă dacă doriți:<br/> <ol> <li>&nbsp;să vedeți mai precis culorile;</li> <li>&nbsp;să eliminați culorile pentru a vă concentra mai bine.</li> </ol>"</string> + <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Corecția culorii poate fi utilă dacă vrei:<br/> <ol> <li>&nbsp;să vezi mai precis culorile;</li> <li>&nbsp;să elimini culorile pentru a te concentra mai bine.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valoare înlocuită de <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Timp aproximativ rămas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -573,10 +573,10 @@ <string name="user_add_user_title" msgid="5457079143694924885">"Adaugi un utilizator nou?"</string> <string name="user_add_user_message_long" msgid="1527434966294733380">"Poți să permiți accesul la acest dispozitiv altor persoane creând utilizatori suplimentari. Fiecare utilizator are propriul spațiu, pe care îl poate personaliza cu aplicații, imagini de fundal etc. De asemenea, utilizatorii pot ajusta setările dispozitivului, cum ar fi setările pentru Wi-Fi, care îi afectează pe toți ceilalți utilizatori.\n\nDupă ce adaugi un utilizator nou, acesta trebuie să-și configureze spațiul.\n\nOricare dintre utilizatori poate actualiza aplicațiile pentru toți ceilalți utilizatori. Este posibil ca setările de accesibilitate și serviciile să nu se transfere la noul utilizator."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"Când adaugi un utilizator nou, acesta trebuie să-și configureze spațiul.\n\nOrice utilizator poate actualiza aplicațiile pentru toți ceilalți utilizatori."</string> - <string name="user_setup_dialog_title" msgid="8037342066381939995">"Configurați utilizatorul acum?"</string> + <string name="user_setup_dialog_title" msgid="8037342066381939995">"Configurezi utilizatorul acum?"</string> <string name="user_setup_dialog_message" msgid="269931619868102841">"Asigură-te că utilizatorul are posibilitatea de a prelua dispozitivul și de a-și configura spațiul"</string> <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Configurezi profilul acum?"</string> - <string name="user_setup_button_setup_now" msgid="1708269547187760639">"Configurați acum"</string> + <string name="user_setup_button_setup_now" msgid="1708269547187760639">"Configurează acum"</string> <string name="user_setup_button_setup_later" msgid="8712980133555493516">"Nu acum"</string> <string name="user_add_user_type_title" msgid="551279664052914497">"Adaugă"</string> <string name="user_new_user_name" msgid="60979820612818840">"Utilizator nou"</string> @@ -593,7 +593,7 @@ <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string> <string name="user_add_user" msgid="7876449291500212468">"Adaugă un utilizator"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string> + <string name="guest_exit_guest" msgid="5908239569510734136">"Șterge invitatul"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"Resetezi sesiunea pentru invitați"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Resetezi invitatul?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Excludeți invitatul?"</string> @@ -604,7 +604,7 @@ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Astfel, va începe o nouă sesiune pentru invitați și se vor șterge toate aplicațiile și datele din sesiunea actuală"</string> <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Ieși din modul pentru invitați?"</string> <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Se vor șterge toate aplicațiile și datele din sesiunea pentru invitați actuală"</string> - <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ieșiți"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ieși"</string> <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Salvezi activitatea invitatului?"</string> <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Salvează activitatea din sesiunea actuală sau șterge aplicațiile și datele"</string> <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Șterge"</string> @@ -613,11 +613,11 @@ <string name="guest_reset_button" msgid="2515069346223503479">"Resetează sesiunea pentru invitați"</string> <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Ieși din modul pentru invitați"</string> <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toate activitățile vor fi șterse la ieșire"</string> - <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Puteți să salvați sau să ștergeți activitatea la ieșire"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Poți să salvezi sau să ștergi activitatea la ieșire"</string> <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Resetează pentru a șterge acum activitatea din sesiune sau salvează ori șterge activitatea la ieșire"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fă o fotografie"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Alege o imagine"</string> - <string name="user_image_photo_selector" msgid="433658323306627093">"Selectați fotografia"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Selectează fotografia"</string> <string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Prea multe încercări incorecte. Datele de pe acest dispozitiv vor fi șterse."</string> <string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Prea multe încercări incorecte. Acest utilizator va fi șters."</string> <string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Prea multe încercări incorecte. Acest profil de serviciu și datele sale vor fi șterse."</string> @@ -662,7 +662,7 @@ <string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"Alege aspectul tastaturii"</string> <string name="keyboard_layout_default_label" msgid="1997292217218546957">"Prestabilit"</string> <string name="turn_screen_on_title" msgid="3266937298097573424">"Activează ecranul"</string> - <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permiteți activarea ecranului"</string> + <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permite activarea ecranului"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permite unei aplicații să activeze ecranul. Dacă acorzi permisiunea, aplicația poate să activeze oricând ecranul, fără intenția ta explicită."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Oprești difuzarea <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Dacă difuzezi <xliff:g id="SWITCHAPP">%1$s</xliff:g> sau schimbi rezultatul, difuzarea actuală se va opri"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index fea7475fc087..5c796af84fef 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java @@ -237,14 +237,10 @@ public class BluetoothUtils { * @return true if it supports advanced metadata, false otherwise. */ public static boolean isAdvancedDetailsHeader(@NonNull BluetoothDevice bluetoothDevice) { - if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI, BT_ADVANCED_HEADER_ENABLED, - true)) { - Log.d(TAG, "isAdvancedDetailsHeader: advancedEnabled is false"); + if (!isAdvancedHeaderEnabled()) { return false; } - // The metadata is for Android R - if (getBooleanMetaData(bluetoothDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) { - Log.d(TAG, "isAdvancedDetailsHeader: untetheredHeadset is true"); + if (isUntetheredHeadset(bluetoothDevice)) { return true; } // The metadata is for Android S @@ -260,6 +256,47 @@ public class BluetoothUtils { } /** + * Check if the Bluetooth device is supports advanced metadata and an untethered headset + * + * @param bluetoothDevice the BluetoothDevice to get metadata + * @return true if it supports advanced metadata and an untethered headset, false otherwise. + */ + public static boolean isAdvancedUntetheredDevice(@NonNull BluetoothDevice bluetoothDevice) { + if (!isAdvancedHeaderEnabled()) { + return false; + } + if (isUntetheredHeadset(bluetoothDevice)) { + return true; + } + // The metadata is for Android S + String deviceType = getStringMetaData(bluetoothDevice, + BluetoothDevice.METADATA_DEVICE_TYPE); + if (TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET)) { + Log.d(TAG, "isAdvancedUntetheredDevice: is untethered device "); + return true; + } + return false; + } + + private static boolean isAdvancedHeaderEnabled() { + if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI, BT_ADVANCED_HEADER_ENABLED, + true)) { + Log.d(TAG, "isAdvancedDetailsHeader: advancedEnabled is false"); + return false; + } + return true; + } + + private static boolean isUntetheredHeadset(@NonNull BluetoothDevice bluetoothDevice) { + // The metadata is for Android R + if (getBooleanMetaData(bluetoothDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) { + Log.d(TAG, "isAdvancedDetailsHeader: untetheredHeadset is true"); + return true; + } + return false; + } + + /** * Create an Icon pointing to a drawable. */ public static IconCompat createIconWithDrawable(Drawable drawable) { diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java index 1be9d76cf3eb..39034047d6eb 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java @@ -59,14 +59,14 @@ public class BluetoothMediaDevice extends MediaDevice { @Override public Drawable getIcon() { - return BluetoothUtils.isAdvancedDetailsHeader(mCachedDevice.getDevice()) + return BluetoothUtils.isAdvancedUntetheredDevice(mCachedDevice.getDevice()) ? mContext.getDrawable(R.drawable.ic_earbuds_advanced) : BluetoothUtils.getBtClassDrawableWithDescription(mContext, mCachedDevice).first; } @Override public Drawable getIconWithoutBackground() { - return BluetoothUtils.isAdvancedDetailsHeader(mCachedDevice.getDevice()) + return BluetoothUtils.isAdvancedUntetheredDevice(mCachedDevice.getDevice()) ? mContext.getDrawable(R.drawable.ic_earbuds_advanced) : BluetoothUtils.getBtClassDrawableWithDescription(mContext, mCachedDevice).first; } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index 58c15eb8073c..7ec0fcdfeb64 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -96,14 +96,14 @@ public class InfoMediaManager extends MediaManager { public void startScan() { mMediaDevices.clear(); mRouterManager.registerCallback(mExecutor, mMediaRouterCallback); - mRouterManager.startScan(); + mRouterManager.registerScanRequest(); refreshDevices(); } @Override public void stopScan() { mRouterManager.unregisterCallback(mMediaRouterCallback); - mRouterManager.stopScan(); + mRouterManager.unregisterScanRequest(); } /** diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java index 1c0ea1a1f4b0..ca14573c95e1 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java @@ -205,4 +205,45 @@ public class BluetoothUtilsTest { public void isAdvancedDetailsHeader_noMetadata_returnFalse() { assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(false); } + + @Test + public void isAdvancedUntetheredDevice_untetheredHeadset_returnTrue() { + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn( + BOOL_METADATA.getBytes()); + + assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(true); + } + + @Test + public void isAdvancedUntetheredDevice_deviceTypeUntetheredHeadset_returnTrue() { + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn( + BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes()); + + assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(true); + } + + @Test + public void isAdvancedUntetheredDevice_deviceTypeWatch_returnFalse() { + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn( + BluetoothDevice.DEVICE_TYPE_WATCH.getBytes()); + + assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(false); + } + + @Test + public void isAdvancedUntetheredDevice_deviceTypeDefault_returnFalse() { + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn( + BluetoothDevice.DEVICE_TYPE_DEFAULT.getBytes()); + + assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(false); + } + + @Test + public void isAdvancedUntetheredDevice_noMetadata_returnFalse() { + assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(false); + } } diff --git a/packages/Shell/res/values-ro/strings.xml b/packages/Shell/res/values-ro/strings.xml index 73322d6bd721..56e9ee0d1350 100644 --- a/packages/Shell/res/values-ro/strings.xml +++ b/packages/Shell/res/values-ro/strings.xml @@ -25,7 +25,7 @@ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Raportul de eroare va apărea curând pe telefon"</string> <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Selectează pentru a trimite raportul de eroare"</string> <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Atinge pentru a trimite raportul de eroare"</string> - <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Selectează pentru a trimite raportul de eroare fără captură de ecran sau așteptați finalizarea acesteia"</string> + <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Selectează pentru a trimite raportul fără captură de ecran sau așteaptă finalizarea acesteia"</string> <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Atinge ca să trimiți raportul de eroare fără captură de ecran sau așteaptă finalizarea acesteia"</string> <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Atinge ca să trimiți raportul de eroare fără captură de ecran sau așteaptă finalizarea acesteia"</string> <string name="bugreport_confirm" msgid="5917407234515812495">"Rapoartele despre erori conțin date din diferite fișiere de jurnal ale sistemului. Acestea pot include date pe care le poți considera sensibile (cum ar fi utilizarea aplicației și date despre locație). Permite accesul la rapoartele despre erori numai aplicațiilor și persoanelor în care ai încredere."</string> diff --git a/packages/SimAppDialog/res/values-ro/strings.xml b/packages/SimAppDialog/res/values-ro/strings.xml index 21663d125f7f..2117191ca3d3 100644 --- a/packages/SimAppDialog/res/values-ro/strings.xml +++ b/packages/SimAppDialog/res/values-ro/strings.xml @@ -18,9 +18,9 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_name" msgid="8898068901680117589">"Sim App Dialog"</string> - <string name="install_carrier_app_title" msgid="334729104862562585">"Activați serviciul mobil"</string> - <string name="install_carrier_app_description" msgid="4014303558674923797">"Pentru ca noul card SIM să funcționeze corect, va trebui să instalați aplicația <xliff:g id="ID_1">%1$s</xliff:g>"</string> - <string name="install_carrier_app_description_default" msgid="7356830245205847840">"Pentru ca noul card SIM să funcționeze corect, va trebui să instalați aplicația operatorului"</string> + <string name="install_carrier_app_title" msgid="334729104862562585">"Activează serviciul mobil"</string> + <string name="install_carrier_app_description" msgid="4014303558674923797">"Pentru ca noul card SIM să funcționeze corect, va trebui să instalezi aplicația <xliff:g id="ID_1">%1$s</xliff:g>"</string> + <string name="install_carrier_app_description_default" msgid="7356830245205847840">"Pentru ca noul card SIM să funcționeze corect, va trebui să instalezi aplicația operatorului"</string> <string name="install_carrier_app_defer_action" msgid="2558576736886876209">"Nu acum"</string> - <string name="install_carrier_app_download_action" msgid="7859229305958538064">"Descărcați aplicația"</string> + <string name="install_carrier_app_download_action" msgid="7859229305958538064">"Descarcă aplicația"</string> </resources> diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index f50dc74210e6..df6f08df7c56 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -124,6 +124,7 @@ android_library { "dagger2", "jsr330", "lottie", + "LowLightDreamLib", ], manifest: "AndroidManifest.xml", @@ -227,6 +228,7 @@ android_library { "dagger2", "jsr330", "WindowManager-Shell", + "LowLightDreamLib", ], libs: [ "android.test.runner", diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt new file mode 100644 index 000000000000..19624e605be8 --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.compose.layout.pager + +import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.DecayAnimationSpec +import androidx.compose.animation.rememberSplineBasedDecay +import androidx.compose.foundation.gestures.FlingBehavior +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.Velocity +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.filter + +/** Library-wide switch to turn on debug logging. */ +internal const val DebugLog = false + +@RequiresOptIn(message = "Accompanist Pager is experimental. The API may be changed in the future.") +@Retention(AnnotationRetention.BINARY) +annotation class ExperimentalPagerApi + +/** Contains the default values used by [HorizontalPager] and [VerticalPager]. */ +@ExperimentalPagerApi +object PagerDefaults { + /** + * Remember the default [FlingBehavior] that represents the scroll curve. + * + * @param state The [PagerState] to update. + * @param decayAnimationSpec The decay animation spec to use for decayed flings. + * @param snapAnimationSpec The animation spec to use when snapping. + */ + @Composable + fun flingBehavior( + state: PagerState, + decayAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(), + snapAnimationSpec: AnimationSpec<Float> = SnappingFlingBehaviorDefaults.snapAnimationSpec, + ): FlingBehavior = + rememberSnappingFlingBehavior( + lazyListState = state.lazyListState, + decayAnimationSpec = decayAnimationSpec, + snapAnimationSpec = snapAnimationSpec, + ) + + @Deprecated( + "Replaced with PagerDefaults.flingBehavior()", + ReplaceWith("PagerDefaults.flingBehavior(state, decayAnimationSpec, snapAnimationSpec)") + ) + @Composable + fun rememberPagerFlingConfig( + state: PagerState, + decayAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(), + snapAnimationSpec: AnimationSpec<Float> = SnappingFlingBehaviorDefaults.snapAnimationSpec, + ): FlingBehavior = flingBehavior(state, decayAnimationSpec, snapAnimationSpec) +} + +/** + * A horizontally scrolling layout that allows users to flip between items to the left and right. + * + * @sample com.google.accompanist.sample.pager.HorizontalPagerSample + * + * @param count the number of pages. + * @param modifier the modifier to apply to this layout. + * @param state the state object to be used to control or observe the pager's state. + * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be + * composed from the end to the start and [PagerState.currentPage] == 0 will mean the first item is + * located at the end. + * @param itemSpacing horizontal spacing to add between items. + * @param flingBehavior logic describing fling behavior. + * @param key the scroll position will be maintained based on the key, which means if you add/remove + * items before the current visible item the item with the given key will be kept as the first + * visible one. + * @param content a block which describes the content. Inside this block you can reference + * [PagerScope.currentPage] and other properties in [PagerScope]. + */ +@ExperimentalPagerApi +@Composable +fun HorizontalPager( + count: Int, + modifier: Modifier = Modifier, + state: PagerState = rememberPagerState(), + reverseLayout: Boolean = false, + itemSpacing: Dp = 0.dp, + flingBehavior: FlingBehavior = PagerDefaults.flingBehavior(state), + verticalAlignment: Alignment.Vertical = Alignment.CenterVertically, + key: ((page: Int) -> Any)? = null, + contentPadding: PaddingValues = PaddingValues(0.dp), + content: @Composable PagerScope.(page: Int) -> Unit, +) { + Pager( + count = count, + state = state, + modifier = modifier, + isVertical = false, + reverseLayout = reverseLayout, + itemSpacing = itemSpacing, + verticalAlignment = verticalAlignment, + flingBehavior = flingBehavior, + key = key, + contentPadding = contentPadding, + content = content + ) +} + +/** + * A vertically scrolling layout that allows users to flip between items to the top and bottom. + * + * @sample com.google.accompanist.sample.pager.VerticalPagerSample + * + * @param count the number of pages. + * @param modifier the modifier to apply to this layout. + * @param state the state object to be used to control or observe the pager's state. + * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be + * composed from the bottom to the top and [PagerState.currentPage] == 0 will mean the first item is + * located at the bottom. + * @param itemSpacing vertical spacing to add between items. + * @param flingBehavior logic describing fling behavior. + * @param key the scroll position will be maintained based on the key, which means if you add/remove + * items before the current visible item the item with the given key will be kept as the first + * visible one. + * @param content a block which describes the content. Inside this block you can reference + * [PagerScope.currentPage] and other properties in [PagerScope]. + */ +@ExperimentalPagerApi +@Composable +fun VerticalPager( + count: Int, + modifier: Modifier = Modifier, + state: PagerState = rememberPagerState(), + reverseLayout: Boolean = false, + itemSpacing: Dp = 0.dp, + flingBehavior: FlingBehavior = PagerDefaults.flingBehavior(state), + horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, + key: ((page: Int) -> Any)? = null, + contentPadding: PaddingValues = PaddingValues(0.dp), + content: @Composable PagerScope.(page: Int) -> Unit, +) { + Pager( + count = count, + state = state, + modifier = modifier, + isVertical = true, + reverseLayout = reverseLayout, + itemSpacing = itemSpacing, + horizontalAlignment = horizontalAlignment, + flingBehavior = flingBehavior, + key = key, + contentPadding = contentPadding, + content = content + ) +} + +@ExperimentalPagerApi +@Composable +internal fun Pager( + count: Int, + modifier: Modifier, + state: PagerState, + reverseLayout: Boolean, + itemSpacing: Dp, + isVertical: Boolean, + flingBehavior: FlingBehavior, + key: ((page: Int) -> Any)?, + contentPadding: PaddingValues, + verticalAlignment: Alignment.Vertical = Alignment.CenterVertically, + horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, + content: @Composable PagerScope.(page: Int) -> Unit, +) { + require(count >= 0) { "pageCount must be >= 0" } + + // Provide our PagerState with access to the SnappingFlingBehavior animation target + // TODO: can this be done in a better way? + state.flingAnimationTarget = { (flingBehavior as? SnappingFlingBehavior)?.animationTarget } + + LaunchedEffect(count) { + state.currentPage = minOf(count - 1, state.currentPage).coerceAtLeast(0) + } + + // Once a fling (scroll) has finished, notify the state + LaunchedEffect(state) { + // When a 'scroll' has finished, notify the state + snapshotFlow { state.isScrollInProgress } + .filter { !it } + .collect { state.onScrollFinished() } + } + + val pagerScope = remember(state) { PagerScopeImpl(state) } + + // We only consume nested flings in the main-axis, allowing cross-axis flings to propagate + // as normal + val consumeFlingNestedScrollConnection = + ConsumeFlingNestedScrollConnection( + consumeHorizontal = !isVertical, + consumeVertical = isVertical, + ) + + if (isVertical) { + LazyColumn( + state = state.lazyListState, + verticalArrangement = Arrangement.spacedBy(itemSpacing, verticalAlignment), + horizontalAlignment = horizontalAlignment, + flingBehavior = flingBehavior, + reverseLayout = reverseLayout, + contentPadding = contentPadding, + modifier = modifier, + ) { + items( + count = count, + key = key, + ) { page -> + Box( + Modifier + // We don't any nested flings to continue in the pager, so we add a + // connection which consumes them. + // See: https://github.com/google/accompanist/issues/347 + .nestedScroll(connection = consumeFlingNestedScrollConnection) + // Constraint the content to be <= than the size of the pager. + .fillParentMaxHeight() + .wrapContentSize() + ) { pagerScope.content(page) } + } + } + } else { + LazyRow( + state = state.lazyListState, + verticalAlignment = verticalAlignment, + horizontalArrangement = Arrangement.spacedBy(itemSpacing, horizontalAlignment), + flingBehavior = flingBehavior, + reverseLayout = reverseLayout, + contentPadding = contentPadding, + modifier = modifier, + ) { + items( + count = count, + key = key, + ) { page -> + Box( + Modifier + // We don't any nested flings to continue in the pager, so we add a + // connection which consumes them. + // See: https://github.com/google/accompanist/issues/347 + .nestedScroll(connection = consumeFlingNestedScrollConnection) + // Constraint the content to be <= than the size of the pager. + .fillParentMaxWidth() + .wrapContentSize() + ) { pagerScope.content(page) } + } + } + } +} + +private class ConsumeFlingNestedScrollConnection( + private val consumeHorizontal: Boolean, + private val consumeVertical: Boolean, +) : NestedScrollConnection { + override fun onPostScroll( + consumed: Offset, + available: Offset, + source: NestedScrollSource + ): Offset = + when (source) { + // We can consume all resting fling scrolls so that they don't propagate up to the + // Pager + NestedScrollSource.Fling -> available.consume(consumeHorizontal, consumeVertical) + else -> Offset.Zero + } + + override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { + // We can consume all post fling velocity on the main-axis + // so that it doesn't propagate up to the Pager + return available.consume(consumeHorizontal, consumeVertical) + } +} + +private fun Offset.consume( + consumeHorizontal: Boolean, + consumeVertical: Boolean, +): Offset = + Offset( + x = if (consumeHorizontal) this.x else 0f, + y = if (consumeVertical) this.y else 0f, + ) + +private fun Velocity.consume( + consumeHorizontal: Boolean, + consumeVertical: Boolean, +): Velocity = + Velocity( + x = if (consumeHorizontal) this.x else 0f, + y = if (consumeVertical) this.y else 0f, + ) + +/** Scope for [HorizontalPager] content. */ +@ExperimentalPagerApi +@Stable +interface PagerScope { + /** Returns the current selected page */ + val currentPage: Int + + /** The current offset from the start of [currentPage], as a ratio of the page width. */ + val currentPageOffset: Float +} + +@ExperimentalPagerApi +private class PagerScopeImpl( + private val state: PagerState, +) : PagerScope { + override val currentPage: Int + get() = state.currentPage + override val currentPageOffset: Float + get() = state.currentPageOffset +} + +/** + * Calculate the offset for the given [page] from the current scroll position. This is useful when + * using the scroll position to apply effects or animations to items. + * + * The returned offset can positive or negative, depending on whether which direction the [page] is + * compared to the current scroll position. + * + * @sample com.google.accompanist.sample.pager.HorizontalPagerWithOffsetTransition + */ +@ExperimentalPagerApi +fun PagerScope.calculateCurrentOffsetForPage(page: Int): Float { + return (currentPage + currentPageOffset) - page +} diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt new file mode 100644 index 000000000000..288c26eb1199 --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.compose.layout.pager + +import androidx.annotation.FloatRange +import androidx.annotation.IntRange +import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.spring +import androidx.compose.foundation.MutatePriority +import androidx.compose.foundation.gestures.ScrollScope +import androidx.compose.foundation.gestures.ScrollableState +import androidx.compose.foundation.interaction.InteractionSource +import androidx.compose.foundation.lazy.LazyListItemInfo +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.listSaver +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import kotlin.math.absoluteValue +import kotlin.math.roundToInt + +@Deprecated( + "Replaced with rememberPagerState(initialPage) and count parameter on Pager composables", + ReplaceWith("rememberPagerState(initialPage)"), + level = DeprecationLevel.ERROR, +) +@Suppress("UNUSED_PARAMETER", "NOTHING_TO_INLINE") +@ExperimentalPagerApi +@Composable +inline fun rememberPagerState( + @IntRange(from = 0) pageCount: Int, + @IntRange(from = 0) initialPage: Int = 0, + @FloatRange(from = 0.0, to = 1.0) initialPageOffset: Float = 0f, + @IntRange(from = 1) initialOffscreenLimit: Int = 1, + infiniteLoop: Boolean = false +): PagerState { + return rememberPagerState(initialPage = initialPage) +} + +/** + * Creates a [PagerState] that is remembered across compositions. + * + * Changes to the provided values for [initialPage] will **not** result in the state being recreated + * or changed in any way if it has already been created. + * + * @param initialPage the initial value for [PagerState.currentPage] + */ +@ExperimentalPagerApi +@Composable +fun rememberPagerState( + @IntRange(from = 0) initialPage: Int = 0, +): PagerState = + rememberSaveable(saver = PagerState.Saver) { + PagerState( + currentPage = initialPage, + ) + } + +/** + * A state object that can be hoisted to control and observe scrolling for [HorizontalPager]. + * + * In most cases, this will be created via [rememberPagerState]. + * + * @param currentPage the initial value for [PagerState.currentPage] + */ +@ExperimentalPagerApi +@Stable +class PagerState( + @IntRange(from = 0) currentPage: Int = 0, +) : ScrollableState { + // Should this be public? + internal val lazyListState = LazyListState(firstVisibleItemIndex = currentPage) + + private var _currentPage by mutableStateOf(currentPage) + + private val currentLayoutPageInfo: LazyListItemInfo? + get() = + lazyListState.layoutInfo.visibleItemsInfo + .asSequence() + .filter { it.offset <= 0 && it.offset + it.size > 0 } + .lastOrNull() + + private val currentLayoutPageOffset: Float + get() = + currentLayoutPageInfo?.let { current -> + // We coerce since itemSpacing can make the offset > 1f. + // We don't want to count spacing in the offset so cap it to 1f + (-current.offset / current.size.toFloat()).coerceIn(0f, 1f) + } + ?: 0f + + /** + * [InteractionSource] that will be used to dispatch drag events when this list is being + * dragged. If you want to know whether the fling (or animated scroll) is in progress, use + * [isScrollInProgress]. + */ + val interactionSource: InteractionSource + get() = lazyListState.interactionSource + + /** The number of pages to display. */ + @get:IntRange(from = 0) + val pageCount: Int by derivedStateOf { lazyListState.layoutInfo.totalItemsCount } + + /** + * The index of the currently selected page. This may not be the page which is currently + * displayed on screen. + * + * To update the scroll position, use [scrollToPage] or [animateScrollToPage]. + */ + @get:IntRange(from = 0) + var currentPage: Int + get() = _currentPage + internal set(value) { + if (value != _currentPage) { + _currentPage = value + } + } + + /** + * The current offset from the start of [currentPage], as a ratio of the page width. + * + * To update the scroll position, use [scrollToPage] or [animateScrollToPage]. + */ + val currentPageOffset: Float by derivedStateOf { + currentLayoutPageInfo?.let { + // The current page offset is the current layout page delta from `currentPage` + // (which is only updated after a scroll/animation). + // We calculate this by looking at the current layout page + it's offset, + // then subtracting the 'current page'. + it.index + currentLayoutPageOffset - _currentPage + } + ?: 0f + } + + /** The target page for any on-going animations. */ + private var animationTargetPage: Int? by mutableStateOf(null) + + internal var flingAnimationTarget: (() -> Int?)? by mutableStateOf(null) + + /** + * The target page for any on-going animations or scrolls by the user. Returns the current page + * if a scroll or animation is not currently in progress. + */ + val targetPage: Int + get() = + animationTargetPage + ?: flingAnimationTarget?.invoke() + ?: when { + // If a scroll isn't in progress, return the current page + !isScrollInProgress -> currentPage + // If the offset is 0f (or very close), return the current page + currentPageOffset.absoluteValue < 0.001f -> currentPage + // If we're offset towards the start, guess the previous page + currentPageOffset < -0.5f -> (currentPage - 1).coerceAtLeast(0) + // If we're offset towards the end, guess the next page + else -> (currentPage + 1).coerceAtMost(pageCount - 1) + } + + @Deprecated( + "Replaced with animateScrollToPage(page, pageOffset)", + ReplaceWith("animateScrollToPage(page = page, pageOffset = pageOffset)") + ) + @Suppress("UNUSED_PARAMETER") + suspend fun animateScrollToPage( + @IntRange(from = 0) page: Int, + @FloatRange(from = 0.0, to = 1.0) pageOffset: Float = 0f, + animationSpec: AnimationSpec<Float> = spring(), + initialVelocity: Float = 0f, + skipPages: Boolean = true, + ) { + animateScrollToPage(page = page, pageOffset = pageOffset) + } + + /** + * Animate (smooth scroll) to the given page to the middle of the viewport. + * + * Cancels the currently running scroll, if any, and suspends until the cancellation is + * complete. + * + * @param page the page to animate to. Must be between 0 and [pageCount] (inclusive). + * @param pageOffset the percentage of the page width to offset, from the start of [page]. Must + * be in the range 0f..1f. + */ + suspend fun animateScrollToPage( + @IntRange(from = 0) page: Int, + @FloatRange(from = 0.0, to = 1.0) pageOffset: Float = 0f, + ) { + requireCurrentPage(page, "page") + requireCurrentPageOffset(pageOffset, "pageOffset") + try { + animationTargetPage = page + + if (pageOffset <= 0.005f) { + // If the offset is (close to) zero, just call animateScrollToItem and we're done + lazyListState.animateScrollToItem(index = page) + } else { + // Else we need to figure out what the offset is in pixels... + + var target = + lazyListState.layoutInfo.visibleItemsInfo.firstOrNull { it.index == page } + + if (target != null) { + // If we have access to the target page layout, we can calculate the pixel + // offset from the size + lazyListState.animateScrollToItem( + index = page, + scrollOffset = (target.size * pageOffset).roundToInt() + ) + } else { + // If we don't, we use the current page size as a guide + val currentSize = currentLayoutPageInfo!!.size + lazyListState.animateScrollToItem( + index = page, + scrollOffset = (currentSize * pageOffset).roundToInt() + ) + + // The target should be visible now + target = lazyListState.layoutInfo.visibleItemsInfo.first { it.index == page } + + if (target.size != currentSize) { + // If the size we used for calculating the offset differs from the actual + // target page size, we need to scroll again. This doesn't look great, + // but there's not much else we can do. + lazyListState.animateScrollToItem( + index = page, + scrollOffset = (target.size * pageOffset).roundToInt() + ) + } + } + } + } finally { + // We need to manually call this, as the `animateScrollToItem` call above will happen + // in 1 frame, which is usually too fast for the LaunchedEffect in Pager to detect + // the change. This is especially true when running unit tests. + onScrollFinished() + } + } + + /** + * Instantly brings the item at [page] to the middle of the viewport. + * + * Cancels the currently running scroll, if any, and suspends until the cancellation is + * complete. + * + * @param page the page to snap to. Must be between 0 and [pageCount] (inclusive). + */ + suspend fun scrollToPage( + @IntRange(from = 0) page: Int, + @FloatRange(from = 0.0, to = 1.0) pageOffset: Float = 0f, + ) { + requireCurrentPage(page, "page") + requireCurrentPageOffset(pageOffset, "pageOffset") + try { + animationTargetPage = page + + // First scroll to the given page. It will now be laid out at offset 0 + lazyListState.scrollToItem(index = page) + + // If we have a start spacing, we need to offset (scroll) by that too + if (pageOffset > 0.0001f) { + scroll { currentLayoutPageInfo?.let { scrollBy(it.size * pageOffset) } } + } + } finally { + // We need to manually call this, as the `scroll` call above will happen in 1 frame, + // which is usually too fast for the LaunchedEffect in Pager to detect the change. + // This is especially true when running unit tests. + onScrollFinished() + } + } + + internal fun onScrollFinished() { + // Then update the current page to our layout page + currentPage = currentLayoutPageInfo?.index ?: 0 + // Clear the animation target page + animationTargetPage = null + } + + override suspend fun scroll( + scrollPriority: MutatePriority, + block: suspend ScrollScope.() -> Unit + ) = lazyListState.scroll(scrollPriority, block) + + override fun dispatchRawDelta(delta: Float): Float { + return lazyListState.dispatchRawDelta(delta) + } + + override val isScrollInProgress: Boolean + get() = lazyListState.isScrollInProgress + + override fun toString(): String = + "PagerState(" + + "pageCount=$pageCount, " + + "currentPage=$currentPage, " + + "currentPageOffset=$currentPageOffset" + + ")" + + private fun requireCurrentPage(value: Int, name: String) { + if (pageCount == 0) { + require(value == 0) { "$name must be 0 when pageCount is 0" } + } else { + require(value in 0 until pageCount) { "$name[$value] must be >= 0 and < pageCount" } + } + } + + private fun requireCurrentPageOffset(value: Float, name: String) { + if (pageCount == 0) { + require(value == 0f) { "$name must be 0f when pageCount is 0" } + } else { + require(value in 0f..1f) { "$name must be >= 0 and <= 1" } + } + } + + companion object { + /** The default [Saver] implementation for [PagerState]. */ + val Saver: Saver<PagerState, *> = + listSaver( + save = { + listOf<Any>( + it.currentPage, + ) + }, + restore = { + PagerState( + currentPage = it[0] as Int, + ) + } + ) + } +} diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt new file mode 100644 index 000000000000..0b53f5324a7d --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.compose.layout.pager + +import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.AnimationState +import androidx.compose.animation.core.DecayAnimationSpec +import androidx.compose.animation.core.animateDecay +import androidx.compose.animation.core.animateTo +import androidx.compose.animation.core.calculateTargetValue +import androidx.compose.animation.core.spring +import androidx.compose.animation.rememberSplineBasedDecay +import androidx.compose.foundation.gestures.FlingBehavior +import androidx.compose.foundation.gestures.ScrollScope +import androidx.compose.foundation.lazy.LazyListItemInfo +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import kotlin.math.abs + +/** Default values used for [SnappingFlingBehavior] & [rememberSnappingFlingBehavior]. */ +internal object SnappingFlingBehaviorDefaults { + /** TODO */ + val snapAnimationSpec: AnimationSpec<Float> = spring(stiffness = 600f) +} + +/** + * Create and remember a snapping [FlingBehavior] to be used with [LazyListState]. + * + * TODO: move this to a new module and make it public + * + * @param lazyListState The [LazyListState] to update. + * @param decayAnimationSpec The decay animation spec to use for decayed flings. + * @param snapAnimationSpec The animation spec to use when snapping. + */ +@Composable +internal fun rememberSnappingFlingBehavior( + lazyListState: LazyListState, + decayAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(), + snapAnimationSpec: AnimationSpec<Float> = SnappingFlingBehaviorDefaults.snapAnimationSpec, +): SnappingFlingBehavior = + remember(lazyListState, decayAnimationSpec, snapAnimationSpec) { + SnappingFlingBehavior( + lazyListState = lazyListState, + decayAnimationSpec = decayAnimationSpec, + snapAnimationSpec = snapAnimationSpec, + ) + } + +/** + * A snapping [FlingBehavior] for [LazyListState]. Typically this would be created via + * [rememberSnappingFlingBehavior]. + * + * @param lazyListState The [LazyListState] to update. + * @param decayAnimationSpec The decay animation spec to use for decayed flings. + * @param snapAnimationSpec The animation spec to use when snapping. + */ +internal class SnappingFlingBehavior( + private val lazyListState: LazyListState, + private val decayAnimationSpec: DecayAnimationSpec<Float>, + private val snapAnimationSpec: AnimationSpec<Float>, +) : FlingBehavior { + /** The target item index for any on-going animations. */ + var animationTarget: Int? by mutableStateOf(null) + private set + + override suspend fun ScrollScope.performFling(initialVelocity: Float): Float { + val itemInfo = currentItemInfo ?: return initialVelocity + + // If the decay fling can scroll past the current item, fling with decay + return if (decayAnimationSpec.canFlingPastCurrentItem(itemInfo, initialVelocity)) { + performDecayFling(initialVelocity, itemInfo) + } else { + // Otherwise we 'spring' to current/next item + performSpringFling( + index = + when { + // If the velocity is greater than 1 item per second (velocity is px/s), + // spring + // in the relevant direction + initialVelocity > itemInfo.size -> { + (itemInfo.index + 1).coerceAtMost( + lazyListState.layoutInfo.totalItemsCount - 1 + ) + } + initialVelocity < -itemInfo.size -> itemInfo.index + // If the velocity is 0 (or less than the size of the item), spring to + // whichever item is closest to the snap point + itemInfo.offset < -itemInfo.size / 2 -> itemInfo.index + 1 + else -> itemInfo.index + }, + initialVelocity = initialVelocity, + ) + } + } + + private suspend fun ScrollScope.performDecayFling( + initialVelocity: Float, + startItem: LazyListItemInfo, + ): Float { + val index = + when { + initialVelocity > 0 -> startItem.index + 1 + else -> startItem.index + } + val forward = index > (currentItemInfo?.index ?: return initialVelocity) + + // Update the animationTarget + animationTarget = index + + var velocityLeft = initialVelocity + var lastValue = 0f + AnimationState( + initialValue = 0f, + initialVelocity = initialVelocity, + ) + .animateDecay(decayAnimationSpec) { + val delta = value - lastValue + val consumed = scrollBy(delta) + lastValue = value + velocityLeft = this.velocity + + val current = currentItemInfo + if (current == null) { + cancelAnimation() + return@animateDecay + } + + if ( + !forward && + (current.index < index || current.index == index && current.offset >= 0) + ) { + // 'snap back' to the item as we may have scrolled past it + scrollBy(lazyListState.calculateScrollOffsetToItem(index).toFloat()) + cancelAnimation() + } else if ( + forward && + (current.index > index || current.index == index && current.offset <= 0) + ) { + // 'snap back' to the item as we may have scrolled past it + scrollBy(lazyListState.calculateScrollOffsetToItem(index).toFloat()) + cancelAnimation() + } else if (abs(delta - consumed) > 0.5f) { + // avoid rounding errors and stop if anything is unconsumed + cancelAnimation() + } + } + animationTarget = null + return velocityLeft + } + + private suspend fun ScrollScope.performSpringFling( + index: Int, + scrollOffset: Int = 0, + initialVelocity: Float = 0f, + ): Float { + // If we don't have a current layout, we can't snap + val initialItem = currentItemInfo ?: return initialVelocity + + val forward = index > initialItem.index + // We add 10% on to the size of the current item, to compensate for any item spacing, etc + val target = (if (forward) initialItem.size else -initialItem.size) * 1.1f + + // Update the animationTarget + animationTarget = index + + var velocityLeft = initialVelocity + var lastValue = 0f + AnimationState( + initialValue = 0f, + initialVelocity = initialVelocity, + ) + .animateTo( + targetValue = target, + animationSpec = snapAnimationSpec, + ) { + // Springs can overshoot their target, clamp to the desired range + val coercedValue = + if (forward) { + value.coerceAtMost(target) + } else { + value.coerceAtLeast(target) + } + val delta = coercedValue - lastValue + val consumed = scrollBy(delta) + lastValue = coercedValue + velocityLeft = this.velocity + + val current = currentItemInfo + if (current == null) { + cancelAnimation() + return@animateTo + } + + if (scrolledPastItem(initialVelocity, current, index, scrollOffset)) { + // If we've scrolled to/past the item, stop the animation. We may also need to + // 'snap back' to the item as we may have scrolled past it + scrollBy(lazyListState.calculateScrollOffsetToItem(index).toFloat()) + cancelAnimation() + } else if (abs(delta - consumed) > 0.5f) { + // avoid rounding errors and stop if anything is unconsumed + cancelAnimation() + } + } + animationTarget = null + return velocityLeft + } + + private fun LazyListState.calculateScrollOffsetToItem(index: Int): Int { + return layoutInfo.visibleItemsInfo.firstOrNull { it.index == index }?.offset ?: 0 + } + + private val currentItemInfo: LazyListItemInfo? + get() = + lazyListState.layoutInfo.visibleItemsInfo + .asSequence() + .filter { it.offset <= 0 && it.offset + it.size > 0 } + .lastOrNull() +} + +private fun scrolledPastItem( + initialVelocity: Float, + currentItem: LazyListItemInfo, + targetIndex: Int, + targetScrollOffset: Int = 0, +): Boolean { + return if (initialVelocity > 0) { + // forward + currentItem.index > targetIndex || + (currentItem.index == targetIndex && currentItem.offset <= targetScrollOffset) + } else { + // backwards + currentItem.index < targetIndex || + (currentItem.index == targetIndex && currentItem.offset >= targetScrollOffset) + } +} + +private fun DecayAnimationSpec<Float>.canFlingPastCurrentItem( + currentItem: LazyListItemInfo, + initialVelocity: Float, +): Boolean { + val targetValue = + calculateTargetValue( + initialValue = currentItem.offset.toFloat(), + initialVelocity = initialVelocity, + ) + return when { + // forward. We add 10% onto the size to cater for any item spacing + initialVelocity > 0 -> targetValue <= -(currentItem.size * 1.1f) + // backwards. We add 10% onto the size to cater for any item spacing + else -> targetValue >= (currentItem.size * 0.1f) + } +} diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt new file mode 100644 index 000000000000..3b13c0b78cbe --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.compose.modifiers + +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.LayoutModifier +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.MeasureScope +import androidx.compose.ui.platform.InspectorInfo +import androidx.compose.ui.platform.InspectorValueInfo +import androidx.compose.ui.platform.debugInspectorInfo +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.constrainHeight +import androidx.compose.ui.unit.constrainWidth +import androidx.compose.ui.unit.offset + +// This file was mostly copy/pasted from by androidx.compose.foundation.layout.Padding.kt and +// contains modifiers with lambda parameters to change the padding of a Composable without +// triggering recomposition when the paddings change. +// +// These should be used instead of the traditional size modifiers when the size changes often, for +// instance when it is animated. +// +// TODO(b/247473910): Remove these modifiers once they can be fully replaced by layout animations +// APIs. + +/** @see androidx.compose.foundation.layout.padding */ +fun Modifier.padding( + start: Density.() -> Int = PaddingUnspecified, + top: Density.() -> Int = PaddingUnspecified, + end: Density.() -> Int = PaddingUnspecified, + bottom: Density.() -> Int = PaddingUnspecified, +) = + this.then( + PaddingModifier( + start, + top, + end, + bottom, + rtlAware = true, + inspectorInfo = + debugInspectorInfo { + name = "padding" + properties["start"] = start + properties["top"] = top + properties["end"] = end + properties["bottom"] = bottom + } + ) + ) + +/** @see androidx.compose.foundation.layout.padding */ +fun Modifier.padding( + horizontal: Density.() -> Int = PaddingUnspecified, + vertical: Density.() -> Int = PaddingUnspecified, +): Modifier { + return this.then( + PaddingModifier( + start = horizontal, + top = vertical, + end = horizontal, + bottom = vertical, + rtlAware = true, + inspectorInfo = + debugInspectorInfo { + name = "padding" + properties["horizontal"] = horizontal + properties["vertical"] = vertical + } + ) + ) +} + +private val PaddingUnspecified: Density.() -> Int = { 0 } + +private class PaddingModifier( + val start: Density.() -> Int, + val top: Density.() -> Int, + val end: Density.() -> Int, + val bottom: Density.() -> Int, + val rtlAware: Boolean, + inspectorInfo: InspectorInfo.() -> Unit +) : LayoutModifier, InspectorValueInfo(inspectorInfo) { + override fun MeasureScope.measure( + measurable: Measurable, + constraints: Constraints + ): MeasureResult { + val start = start() + val top = top() + val end = end() + val bottom = bottom() + + val horizontal = start + end + val vertical = top + bottom + + val placeable = measurable.measure(constraints.offset(-horizontal, -vertical)) + + val width = constraints.constrainWidth(placeable.width + horizontal) + val height = constraints.constrainHeight(placeable.height + vertical) + return layout(width, height) { + if (rtlAware) { + placeable.placeRelative(start, top) + } else { + placeable.place(start, top) + } + } + } + + override fun hashCode(): Int { + var result = start.hashCode() + result = 31 * result + top.hashCode() + result = 31 * result + end.hashCode() + result = 31 * result + bottom.hashCode() + result = 31 * result + rtlAware.hashCode() + return result + } + + override fun equals(other: Any?): Boolean { + val otherModifier = other as? PaddingModifier ?: return false + return start == otherModifier.start && + top == otherModifier.top && + end == otherModifier.end && + bottom == otherModifier.bottom && + rtlAware == otherModifier.rtlAware + } +} diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt new file mode 100644 index 000000000000..570d24312c80 --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.compose.modifiers + +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.IntrinsicMeasurable +import androidx.compose.ui.layout.IntrinsicMeasureScope +import androidx.compose.ui.layout.LayoutModifier +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.MeasureScope +import androidx.compose.ui.platform.InspectorInfo +import androidx.compose.ui.platform.InspectorValueInfo +import androidx.compose.ui.platform.debugInspectorInfo +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.constrain +import androidx.compose.ui.unit.constrainHeight +import androidx.compose.ui.unit.constrainWidth + +// This file was mostly copy pasted from androidx.compose.foundation.layout.Size.kt and contains +// modifiers with lambda parameters to change the (min/max) size of a Composable without triggering +// recomposition when the sizes change. +// +// These should be used instead of the traditional size modifiers when the size changes often, for +// instance when it is animated. +// +// TODO(b/247473910): Remove these modifiers once they can be fully replaced by layout animations +// APIs. + +/** @see androidx.compose.foundation.layout.width */ +fun Modifier.width(width: Density.() -> Int) = + this.then( + SizeModifier( + minWidth = width, + maxWidth = width, + enforceIncoming = true, + inspectorInfo = + debugInspectorInfo { + name = "width" + value = width + } + ) + ) + +/** @see androidx.compose.foundation.layout.height */ +fun Modifier.height(height: Density.() -> Int) = + this.then( + SizeModifier( + minHeight = height, + maxHeight = height, + enforceIncoming = true, + inspectorInfo = + debugInspectorInfo { + name = "height" + value = height + } + ) + ) + +/** @see androidx.compose.foundation.layout.size */ +fun Modifier.size(width: Density.() -> Int, height: Density.() -> Int) = + this.then( + SizeModifier( + minWidth = width, + maxWidth = width, + minHeight = height, + maxHeight = height, + enforceIncoming = true, + inspectorInfo = + debugInspectorInfo { + name = "size" + properties["width"] = width + properties["height"] = height + } + ) + ) + +private val SizeUnspecified: Density.() -> Int = { 0 } + +private class SizeModifier( + private val minWidth: Density.() -> Int = SizeUnspecified, + private val minHeight: Density.() -> Int = SizeUnspecified, + private val maxWidth: Density.() -> Int = SizeUnspecified, + private val maxHeight: Density.() -> Int = SizeUnspecified, + private val enforceIncoming: Boolean, + inspectorInfo: InspectorInfo.() -> Unit +) : LayoutModifier, InspectorValueInfo(inspectorInfo) { + private val Density.targetConstraints: Constraints + get() { + val maxWidth = + if (maxWidth != SizeUnspecified) { + maxWidth().coerceAtLeast(0) + } else { + Constraints.Infinity + } + val maxHeight = + if (maxHeight != SizeUnspecified) { + maxHeight().coerceAtLeast(0) + } else { + Constraints.Infinity + } + val minWidth = + if (minWidth != SizeUnspecified) { + minWidth().coerceAtMost(maxWidth).coerceAtLeast(0).let { + if (it != Constraints.Infinity) it else 0 + } + } else { + 0 + } + val minHeight = + if (minHeight != SizeUnspecified) { + minHeight().coerceAtMost(maxHeight).coerceAtLeast(0).let { + if (it != Constraints.Infinity) it else 0 + } + } else { + 0 + } + return Constraints( + minWidth = minWidth, + minHeight = minHeight, + maxWidth = maxWidth, + maxHeight = maxHeight + ) + } + + override fun MeasureScope.measure( + measurable: Measurable, + constraints: Constraints + ): MeasureResult { + val wrappedConstraints = + targetConstraints.let { targetConstraints -> + if (enforceIncoming) { + constraints.constrain(targetConstraints) + } else { + val resolvedMinWidth = + if (minWidth != SizeUnspecified) { + targetConstraints.minWidth + } else { + constraints.minWidth.coerceAtMost(targetConstraints.maxWidth) + } + val resolvedMaxWidth = + if (maxWidth != SizeUnspecified) { + targetConstraints.maxWidth + } else { + constraints.maxWidth.coerceAtLeast(targetConstraints.minWidth) + } + val resolvedMinHeight = + if (minHeight != SizeUnspecified) { + targetConstraints.minHeight + } else { + constraints.minHeight.coerceAtMost(targetConstraints.maxHeight) + } + val resolvedMaxHeight = + if (maxHeight != SizeUnspecified) { + targetConstraints.maxHeight + } else { + constraints.maxHeight.coerceAtLeast(targetConstraints.minHeight) + } + Constraints( + resolvedMinWidth, + resolvedMaxWidth, + resolvedMinHeight, + resolvedMaxHeight + ) + } + } + val placeable = measurable.measure(wrappedConstraints) + return layout(placeable.width, placeable.height) { placeable.placeRelative(0, 0) } + } + + override fun IntrinsicMeasureScope.minIntrinsicWidth( + measurable: IntrinsicMeasurable, + height: Int + ): Int { + val constraints = targetConstraints + return if (constraints.hasFixedWidth) { + constraints.maxWidth + } else { + constraints.constrainWidth(measurable.minIntrinsicWidth(height)) + } + } + + override fun IntrinsicMeasureScope.minIntrinsicHeight( + measurable: IntrinsicMeasurable, + width: Int + ): Int { + val constraints = targetConstraints + return if (constraints.hasFixedHeight) { + constraints.maxHeight + } else { + constraints.constrainHeight(measurable.minIntrinsicHeight(width)) + } + } + + override fun IntrinsicMeasureScope.maxIntrinsicWidth( + measurable: IntrinsicMeasurable, + height: Int + ): Int { + val constraints = targetConstraints + return if (constraints.hasFixedWidth) { + constraints.maxWidth + } else { + constraints.constrainWidth(measurable.maxIntrinsicWidth(height)) + } + } + + override fun IntrinsicMeasureScope.maxIntrinsicHeight( + measurable: IntrinsicMeasurable, + width: Int + ): Int { + val constraints = targetConstraints + return if (constraints.hasFixedHeight) { + constraints.maxHeight + } else { + constraints.constrainHeight(measurable.maxIntrinsicHeight(width)) + } + } + + override fun equals(other: Any?): Boolean { + if (other !is SizeModifier) return false + return minWidth == other.minWidth && + minHeight == other.minHeight && + maxWidth == other.maxWidth && + maxHeight == other.maxHeight && + enforceIncoming == other.enforceIncoming + } + + override fun hashCode() = + (((((minWidth.hashCode() * 31 + minHeight.hashCode()) * 31) + maxWidth.hashCode()) * 31) + + maxHeight.hashCode()) * 31 +} diff --git a/packages/SystemUI/compose/gallery/Android.bp b/packages/SystemUI/compose/gallery/Android.bp deleted file mode 100644 index 5a7a1e1807a3..000000000000 --- a/packages/SystemUI/compose/gallery/Android.bp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (C) 2022 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], -} - -android_library { - name: "SystemUIComposeGalleryLib", - manifest: "AndroidManifest.xml", - - srcs: [ - "src/**/*.kt", - ":SystemUI-tests-utils", - ], - - resource_dirs: [ - "res", - ], - - static_libs: [ - "SystemUI-core", - "SystemUIComposeCore", - "SystemUIComposeFeatures", - - "androidx.compose.runtime_runtime", - "androidx.compose.material3_material3", - "androidx.compose.material_material-icons-extended", - "androidx.activity_activity-compose", - "androidx.navigation_navigation-compose", - - "androidx.appcompat_appcompat", - - // TODO(b/240431193): Remove the dependencies and depend on - // SystemUI-test-utils directly. - "androidx.test.runner", - "mockito-target-extended-minus-junit4", - "testables", - "truth-prebuilt", - "androidx.test.uiautomator", - "kotlinx_coroutines_test", - ], - - libs: [ - "android.test.mock", - ], - - kotlincflags: ["-Xjvm-default=all"], -} - -android_app { - name: "SystemUIComposeGallery", - defaults: ["platform_app_defaults"], - manifest: "app/AndroidManifest.xml", - - static_libs: [ - "SystemUIComposeGalleryLib", - ], - - platform_apis: true, - system_ext_specific: true, - certificate: "platform", - privileged: true, - - optimize: { - proguard_flags_files: ["proguard-rules.pro"], - }, - - dxflags: ["--multi-dex"], -} diff --git a/packages/SystemUI/compose/gallery/AndroidManifest.xml b/packages/SystemUI/compose/gallery/AndroidManifest.xml deleted file mode 100644 index 2f30651a6acf..000000000000 --- a/packages/SystemUI/compose/gallery/AndroidManifest.xml +++ /dev/null @@ -1,55 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 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. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - package="com.android.systemui.compose.gallery"> - <!-- To emulate a display size and density. --> - <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> - - <application - android:name="android.app.Application" - android:appComponentFactory="androidx.core.app.AppComponentFactory" - tools:replace="android:name,android:appComponentFactory"> - <!-- Disable providers from SystemUI --> - <provider android:name="com.android.systemui.keyguard.KeyguardSliceProvider" - android:authorities="com.android.systemui.test.keyguard.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> - <provider android:name="com.google.android.systemui.keyguard.KeyguardSliceProviderGoogle" - android:authorities="com.android.systemui.test.keyguard.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> - <provider android:name="com.android.keyguard.clock.ClockOptionsProvider" - android:authorities="com.android.systemui.test.keyguard.clock.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> - <provider android:name="com.android.systemui.people.PeopleProvider" - android:authorities="com.android.systemui.test.people.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> - <provider android:name="androidx.core.content.FileProvider" - android:authorities="com.android.systemui.test.fileprovider.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove"/> - </application> -</manifest> diff --git a/packages/SystemUI/compose/gallery/TEST_MAPPING b/packages/SystemUI/compose/gallery/TEST_MAPPING deleted file mode 100644 index c7f8a9216418..000000000000 --- a/packages/SystemUI/compose/gallery/TEST_MAPPING +++ /dev/null @@ -1,15 +0,0 @@ -{ - "presubmit": [ - { - "name": "SystemUIComposeGalleryTests", - "options": [ - { - "exclude-annotation": "org.junit.Ignore" - }, - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - } - ] - } - ] -}
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/app/AndroidManifest.xml b/packages/SystemUI/compose/gallery/app/AndroidManifest.xml deleted file mode 100644 index 1f3fd8c312d9..000000000000 --- a/packages/SystemUI/compose/gallery/app/AndroidManifest.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 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. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - package="com.android.systemui.compose.gallery.app"> - <application - android:allowBackup="true" - android:icon="@mipmap/ic_launcher" - android:label="@string/app_name" - android:roundIcon="@mipmap/ic_launcher_round" - android:supportsRtl="true" - android:theme="@style/Theme.SystemUI.Gallery" - tools:replace="android:icon,android:theme,android:label"> - <activity - android:name="com.android.systemui.compose.gallery.GalleryActivity" - android:exported="true" - android:label="@string/app_name"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/packages/SystemUI/compose/gallery/proguard-rules.pro b/packages/SystemUI/compose/gallery/proguard-rules.pro deleted file mode 100644 index 481bb4348141..000000000000 --- a/packages/SystemUI/compose/gallery/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/res/drawable-v24/ic_launcher_foreground.xml b/packages/SystemUI/compose/gallery/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 966abaff2074..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ -<vector xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:aapt="http://schemas.android.com/aapt" - android:width="108dp" - android:height="108dp" - android:viewportHeight="108" - android:viewportWidth="108"> - <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z"> - <aapt:attr name="android:fillColor"> - <gradient - android:endX="85.84757" - android:endY="92.4963" - android:startX="42.9492" - android:startY="49.59793" - android:type="linear"> - <item - android:color="#44000000" - android:offset="0.0" /> - <item - android:color="#00000000" - android:offset="1.0" /> - </gradient> - </aapt:attr> - </path> - <path - android:fillColor="#FFFFFF" - android:fillType="nonZero" - android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" - android:strokeColor="#00000000" - android:strokeWidth="1" /> -</vector>
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/res/drawable/ic_launcher_background.xml b/packages/SystemUI/compose/gallery/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 61bb79edb709..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="108dp" - android:height="108dp" - android:viewportHeight="108" - android:viewportWidth="108"> - <path - android:fillColor="#3DDC84" - android:pathData="M0,0h108v108h-108z" /> - <path - android:fillColor="#00000000" - android:pathData="M9,0L9,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M19,0L19,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M29,0L29,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M39,0L39,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M49,0L49,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M59,0L59,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M69,0L69,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M79,0L79,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M89,0L89,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M99,0L99,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,9L108,9" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,19L108,19" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,29L108,29" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,39L108,39" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,49L108,49" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,59L108,59" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,69L108,69" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,79L108,79" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,89L108,89" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,99L108,99" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M19,29L89,29" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M19,39L89,39" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M19,49L89,49" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M19,59L89,59" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M19,69L89,69" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M19,79L89,79" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M29,19L29,89" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M39,19L39,89" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M49,19L49,89" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M59,19L59,89" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M69,19L69,89" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M79,19L79,89" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> -</vector> diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten1.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten1.jpeg Binary files differdeleted file mode 100644 index 6241b0b44bb6..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable/kitten1.jpeg +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten2.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten2.jpeg Binary files differdeleted file mode 100644 index 870ef13ee2d9..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable/kitten2.jpeg +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten3.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten3.jpeg Binary files differdeleted file mode 100644 index bb7261c10033..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable/kitten3.jpeg +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten4.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten4.jpeg Binary files differdeleted file mode 100644 index e34b7ddf58ce..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable/kitten4.jpeg +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten5.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten5.jpeg Binary files differdeleted file mode 100644 index 9cde24be59ef..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable/kitten5.jpeg +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten6.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten6.jpeg Binary files differdeleted file mode 100644 index 17825b639a26..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable/kitten6.jpeg +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher.xml b/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 03eed2533da2..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <background android:drawable="@drawable/ic_launcher_background" /> - <foreground android:drawable="@drawable/ic_launcher_foreground" /> -</adaptive-icon>
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher_round.xml b/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 03eed2533da2..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <background android:drawable="@drawable/ic_launcher_background" /> - <foreground android:drawable="@drawable/ic_launcher_foreground" /> -</adaptive-icon>
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher.webp b/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher.webp Binary files differdeleted file mode 100644 index c209e78ecd37..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher_round.webp b/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher_round.webp Binary files differdeleted file mode 100644 index b2dfe3d1ba5c..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher_round.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher.webp b/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher.webp Binary files differdeleted file mode 100644 index 4f0f1d64e58b..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher_round.webp b/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher_round.webp Binary files differdeleted file mode 100644 index 62b611da0816..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher_round.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher.webp b/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher.webp Binary files differdeleted file mode 100644 index 948a3070fe34..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher_round.webp b/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher_round.webp Binary files differdeleted file mode 100644 index 1b9a6956b3ac..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher_round.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher.webp b/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher.webp Binary files differdeleted file mode 100644 index 28d4b77f9f03..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher_round.webp b/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher_round.webp Binary files differdeleted file mode 100644 index 9287f5083623..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher_round.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher.webp b/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher.webp Binary files differdeleted file mode 100644 index aa7d6427e6fa..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher_round.webp b/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher_round.webp Binary files differdeleted file mode 100644 index 9126ae37cbc3..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher_round.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/values/colors.xml b/packages/SystemUI/compose/gallery/res/values/colors.xml deleted file mode 100644 index a2fcbffc26c0..000000000000 --- a/packages/SystemUI/compose/gallery/res/values/colors.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 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. ---> -<resources> - <color name="ic_launcher_background">#FFFFFF</color> -</resources>
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/res/values/strings.xml b/packages/SystemUI/compose/gallery/res/values/strings.xml deleted file mode 100644 index 86bdb0568837..000000000000 --- a/packages/SystemUI/compose/gallery/res/values/strings.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 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. ---> -<resources> - <!-- Application name [CHAR LIMIT=NONE] --> - <string name="app_name">SystemUI Gallery</string> -</resources>
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/res/values/themes.xml b/packages/SystemUI/compose/gallery/res/values/themes.xml deleted file mode 100644 index 45fa1f5dfb5c..000000000000 --- a/packages/SystemUI/compose/gallery/res/values/themes.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 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. ---> -<resources xmlns:tools="http://schemas.android.com/tools"> - <style name="Theme.SystemUI.Gallery"> - <item name="android:windowActionBar">false</item> - <item name="android:windowNoTitle">true</item> - - <item name="android:statusBarColor" tools:targetApi="l"> - @android:color/transparent - </item> - <item name="android:navigationBarColor" tools:targetApi="l"> - @android:color/transparent - </item> - <item name="android:windowLightStatusBar">true</item> - </style> -</resources> diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ButtonsScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ButtonsScreen.kt deleted file mode 100644 index 881a1def113a..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ButtonsScreen.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2022 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. - * - */ - -@file:OptIn(ExperimentalMaterial3Api::class) - -package com.android.systemui.compose.gallery - -import androidx.compose.foundation.layout.Column -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.android.systemui.compose.SysUiButton -import com.android.systemui.compose.SysUiOutlinedButton -import com.android.systemui.compose.SysUiTextButton - -@Composable -fun ButtonsScreen( - modifier: Modifier = Modifier, -) { - Column( - modifier = modifier, - ) { - SysUiButton( - onClick = {}, - ) { - Text("SysUiButton") - } - - SysUiButton( - onClick = {}, - enabled = false, - ) { - Text("SysUiButton - disabled") - } - - SysUiOutlinedButton( - onClick = {}, - ) { - Text("SysUiOutlinedButton") - } - - SysUiOutlinedButton( - onClick = {}, - enabled = false, - ) { - Text("SysUiOutlinedButton - disabled") - } - - SysUiTextButton( - onClick = {}, - ) { - Text("SysUiTextButton") - } - - SysUiTextButton( - onClick = {}, - enabled = false, - ) { - Text("SysUiTextButton - disabled") - } - } -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ColorsScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ColorsScreen.kt deleted file mode 100644 index dfa1b26f464e..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ColorsScreen.kt +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.compose.gallery - -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import com.android.systemui.compose.theme.LocalAndroidColorScheme - -/** The screen that shows all the Material 3 colors. */ -@Composable -fun MaterialColorsScreen() { - val colors = MaterialTheme.colorScheme - ColorsScreen( - listOf( - "primary" to colors.primary, - "onPrimary" to colors.onPrimary, - "primaryContainer" to colors.primaryContainer, - "onPrimaryContainer" to colors.onPrimaryContainer, - "inversePrimary" to colors.inversePrimary, - "secondary" to colors.secondary, - "onSecondary" to colors.onSecondary, - "secondaryContainer" to colors.secondaryContainer, - "onSecondaryContainer" to colors.onSecondaryContainer, - "tertiary" to colors.tertiary, - "onTertiary" to colors.onTertiary, - "tertiaryContainer" to colors.tertiaryContainer, - "onTertiaryContainer" to colors.onTertiaryContainer, - "background" to colors.background, - "onBackground" to colors.onBackground, - "surface" to colors.surface, - "onSurface" to colors.onSurface, - "surfaceVariant" to colors.surfaceVariant, - "onSurfaceVariant" to colors.onSurfaceVariant, - "inverseSurface" to colors.inverseSurface, - "inverseOnSurface" to colors.inverseOnSurface, - "error" to colors.error, - "onError" to colors.onError, - "errorContainer" to colors.errorContainer, - "onErrorContainer" to colors.onErrorContainer, - "outline" to colors.outline, - ) - ) -} - -/** The screen that shows all the Android colors. */ -@Composable -fun AndroidColorsScreen() { - val colors = LocalAndroidColorScheme.current - ColorsScreen( - listOf( - "colorPrimary" to colors.colorPrimary, - "colorPrimaryDark" to colors.colorPrimaryDark, - "colorAccent" to colors.colorAccent, - "colorAccentPrimary" to colors.colorAccentPrimary, - "colorAccentSecondary" to colors.colorAccentSecondary, - "colorAccentTertiary" to colors.colorAccentTertiary, - "colorAccentPrimaryVariant" to colors.colorAccentPrimaryVariant, - "colorAccentSecondaryVariant" to colors.colorAccentSecondaryVariant, - "colorAccentTertiaryVariant" to colors.colorAccentTertiaryVariant, - "colorSurface" to colors.colorSurface, - "colorSurfaceHighlight" to colors.colorSurfaceHighlight, - "colorSurfaceVariant" to colors.colorSurfaceVariant, - "colorSurfaceHeader" to colors.colorSurfaceHeader, - "colorError" to colors.colorError, - "colorBackground" to colors.colorBackground, - "colorBackgroundFloating" to colors.colorBackgroundFloating, - "panelColorBackground" to colors.panelColorBackground, - "textColorPrimary" to colors.textColorPrimary, - "textColorSecondary" to colors.textColorSecondary, - "textColorTertiary" to colors.textColorTertiary, - "textColorPrimaryInverse" to colors.textColorPrimaryInverse, - "textColorSecondaryInverse" to colors.textColorSecondaryInverse, - "textColorTertiaryInverse" to colors.textColorTertiaryInverse, - "textColorOnAccent" to colors.textColorOnAccent, - "colorForeground" to colors.colorForeground, - "colorForegroundInverse" to colors.colorForegroundInverse, - ) - ) -} - -@Composable -private fun ColorsScreen( - colors: List<Pair<String, Color>>, -) { - LazyColumn( - Modifier.fillMaxWidth(), - ) { - colors.forEach { (name, color) -> item { ColorTile(color, name) } } - } -} - -@Composable -private fun ColorTile( - color: Color, - name: String, -) { - Row( - Modifier.padding(16.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - val shape = RoundedCornerShape(16.dp) - Spacer( - Modifier.border(1.dp, MaterialTheme.colorScheme.onBackground, shape) - .background(color, shape) - .size(64.dp) - ) - Spacer(Modifier.width(16.dp)) - Text(name) - } -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt deleted file mode 100644 index 990d060207df..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt +++ /dev/null @@ -1,210 +0,0 @@ -package com.android.systemui.compose.gallery - -import android.graphics.Point -import android.os.UserHandle -import android.view.Display -import android.view.WindowManagerGlobal -import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.DarkMode -import androidx.compose.material.icons.filled.FormatSize -import androidx.compose.material.icons.filled.FormatTextdirectionLToR -import androidx.compose.material.icons.filled.FormatTextdirectionRToL -import androidx.compose.material.icons.filled.InvertColors -import androidx.compose.material.icons.filled.LightMode -import androidx.compose.material.icons.filled.Smartphone -import androidx.compose.material.icons.filled.Tablet -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.unit.LayoutDirection -import androidx.compose.ui.unit.dp -import kotlin.math.max -import kotlin.math.min - -enum class FontScale(val scale: Float) { - Small(0.85f), - Normal(1f), - Big(1.15f), - Bigger(1.30f), -} - -/** A configuration panel that allows to toggle the theme, font scale and layout direction. */ -@Composable -fun ConfigurationControls( - theme: Theme, - fontScale: FontScale, - layoutDirection: LayoutDirection, - onChangeTheme: () -> Unit, - onChangeLayoutDirection: () -> Unit, - onChangeFontScale: () -> Unit, - modifier: Modifier = Modifier, -) { - // The display we are emulating, if any. - var emulatedDisplayName by rememberSaveable { mutableStateOf<String?>(null) } - val emulatedDisplay = - emulatedDisplayName?.let { name -> EmulatedDisplays.firstOrNull { it.name == name } } - - LaunchedEffect(emulatedDisplay) { - val wm = WindowManagerGlobal.getWindowManagerService() - - val defaultDisplayId = Display.DEFAULT_DISPLAY - if (emulatedDisplay == null) { - wm.clearForcedDisplayDensityForUser(defaultDisplayId, UserHandle.myUserId()) - wm.clearForcedDisplaySize(defaultDisplayId) - } else { - val density = emulatedDisplay.densityDpi - - // Emulate the display and make sure that we use the maximum available space possible. - val initialSize = Point() - wm.getInitialDisplaySize(defaultDisplayId, initialSize) - val width = emulatedDisplay.width - val height = emulatedDisplay.height - val minOfSize = min(width, height) - val maxOfSize = max(width, height) - if (initialSize.x < initialSize.y) { - wm.setForcedDisplaySize(defaultDisplayId, minOfSize, maxOfSize) - } else { - wm.setForcedDisplaySize(defaultDisplayId, maxOfSize, minOfSize) - } - wm.setForcedDisplayDensityForUser(defaultDisplayId, density, UserHandle.myUserId()) - } - } - - // TODO(b/231131244): Fork FlowRow from Accompanist and use that instead to make sure that users - // don't miss any available configuration. - LazyRow(modifier) { - // Dark/light theme. - item { - TextButton(onChangeTheme) { - val text: String - val icon: ImageVector - - when (theme) { - Theme.System -> { - icon = Icons.Default.InvertColors - text = "System" - } - Theme.Dark -> { - icon = Icons.Default.DarkMode - text = "Dark" - } - Theme.Light -> { - icon = Icons.Default.LightMode - text = "Light" - } - } - - Icon(icon, null) - Spacer(Modifier.width(8.dp)) - Text(text) - } - } - - // Font scale. - item { - TextButton(onChangeFontScale) { - Icon(Icons.Default.FormatSize, null) - Spacer(Modifier.width(8.dp)) - - Text(fontScale.name) - } - } - - // Layout direction. - item { - TextButton(onChangeLayoutDirection) { - when (layoutDirection) { - LayoutDirection.Ltr -> { - Icon(Icons.Default.FormatTextdirectionLToR, null) - Spacer(Modifier.width(8.dp)) - Text("LTR") - } - LayoutDirection.Rtl -> { - Icon(Icons.Default.FormatTextdirectionRToL, null) - Spacer(Modifier.width(8.dp)) - Text("RTL") - } - } - } - } - - // Display emulation. - EmulatedDisplays.forEach { display -> - item { - DisplayButton( - display, - emulatedDisplay == display, - { emulatedDisplayName = it?.name }, - ) - } - } - } -} - -@Composable -private fun DisplayButton( - display: EmulatedDisplay, - selected: Boolean, - onChangeEmulatedDisplay: (EmulatedDisplay?) -> Unit, -) { - val onClick = { - if (selected) { - onChangeEmulatedDisplay(null) - } else { - onChangeEmulatedDisplay(display) - } - } - - val content: @Composable RowScope.() -> Unit = { - Icon(display.icon, null) - Spacer(Modifier.width(8.dp)) - Text(display.name) - } - - if (selected) { - Button(onClick, contentPadding = ButtonDefaults.TextButtonContentPadding, content = content) - } else { - TextButton(onClick, content = content) - } -} - -/** The displays that can be emulated from this Gallery app. */ -private val EmulatedDisplays = - listOf( - EmulatedDisplay( - "Phone", - Icons.Default.Smartphone, - width = 1440, - height = 3120, - densityDpi = 560, - ), - EmulatedDisplay( - "Tablet", - Icons.Default.Tablet, - width = 2560, - height = 1600, - densityDpi = 320, - ), - ) - -private data class EmulatedDisplay( - val name: String, - val icon: ImageVector, - val width: Int, - val height: Int, - val densityDpi: Int, -) diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt deleted file mode 100644 index bb2d2feba39f..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.compose.gallery - -import android.app.UiModeManager -import android.content.Context -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.runtime.SideEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.graphics.Color -import androidx.core.view.WindowCompat -import com.android.systemui.compose.rememberSystemUiController - -class GalleryActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - WindowCompat.setDecorFitsSystemWindows(window, false) - val uiModeManager = getSystemService(Context.UI_MODE_SERVICE) as UiModeManager - - setContent { - var theme by rememberSaveable { mutableStateOf(Theme.System) } - val onChangeTheme = { - // Change to the next theme for a toggle behavior. - theme = - when (theme) { - Theme.System -> Theme.Dark - Theme.Dark -> Theme.Light - Theme.Light -> Theme.System - } - } - - val isSystemInDarkTheme = isSystemInDarkTheme() - val isDark = theme == Theme.Dark || (theme == Theme.System && isSystemInDarkTheme) - val useDarkIcons = !isDark - val systemUiController = rememberSystemUiController() - SideEffect { - systemUiController.setSystemBarsColor( - color = Color.Transparent, - darkIcons = useDarkIcons, - ) - - uiModeManager.setApplicationNightMode( - when (theme) { - Theme.System -> UiModeManager.MODE_NIGHT_AUTO - Theme.Dark -> UiModeManager.MODE_NIGHT_YES - Theme.Light -> UiModeManager.MODE_NIGHT_NO - } - ) - } - - GalleryApp(theme, onChangeTheme) - } - } -} - -enum class Theme { - System, - Dark, - Light, -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt deleted file mode 100644 index 6805bf83dff4..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt +++ /dev/null @@ -1,202 +0,0 @@ -package com.android.systemui.compose.gallery - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.systemBarsPadding -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.unit.Density -import androidx.compose.ui.unit.LayoutDirection -import androidx.compose.ui.unit.dp -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.rememberNavController -import com.android.systemui.compose.theme.SystemUITheme - -/** The gallery app screens. */ -object GalleryAppScreens { - private val Typography = ChildScreen("typography") { TypographyScreen() } - private val MaterialColors = ChildScreen("material_colors") { MaterialColorsScreen() } - private val AndroidColors = ChildScreen("android_colors") { AndroidColorsScreen() } - private val Buttons = ChildScreen("buttons") { ButtonsScreen() } - private val ExampleFeature = ChildScreen("example_feature") { ExampleFeatureScreen() } - - private val PeopleEmpty = - ChildScreen("people_empty") { navController -> - EmptyPeopleScreen(onResult = { navController.popBackStack() }) - } - private val PeopleFew = - ChildScreen("people_few") { navController -> - FewPeopleScreen(onResult = { navController.popBackStack() }) - } - private val PeopleFull = - ChildScreen("people_full") { navController -> - FullPeopleScreen(onResult = { navController.popBackStack() }) - } - private val People = - ParentScreen( - "people", - mapOf( - "Empty" to PeopleEmpty, - "Few" to PeopleFew, - "Full" to PeopleFull, - ) - ) - private val UserSwitcherSingleUser = - ChildScreen("user_switcher_single") { navController -> - UserSwitcherScreen( - userCount = 1, - onFinished = navController::popBackStack, - ) - } - private val UserSwitcherThreeUsers = - ChildScreen("user_switcher_three") { navController -> - UserSwitcherScreen( - userCount = 3, - onFinished = navController::popBackStack, - ) - } - private val UserSwitcherFourUsers = - ChildScreen("user_switcher_four") { navController -> - UserSwitcherScreen( - userCount = 4, - onFinished = navController::popBackStack, - ) - } - private val UserSwitcherFiveUsers = - ChildScreen("user_switcher_five") { navController -> - UserSwitcherScreen( - userCount = 5, - onFinished = navController::popBackStack, - ) - } - private val UserSwitcherSixUsers = - ChildScreen("user_switcher_six") { navController -> - UserSwitcherScreen( - userCount = 6, - onFinished = navController::popBackStack, - ) - } - private val UserSwitcher = - ParentScreen( - "user_switcher", - mapOf( - "Single" to UserSwitcherSingleUser, - "Three" to UserSwitcherThreeUsers, - "Four" to UserSwitcherFourUsers, - "Five" to UserSwitcherFiveUsers, - "Six" to UserSwitcherSixUsers, - ) - ) - - val Home = - ParentScreen( - "home", - mapOf( - "Typography" to Typography, - "Material colors" to MaterialColors, - "Android colors" to AndroidColors, - "Example feature" to ExampleFeature, - "Buttons" to Buttons, - "People" to People, - "User Switcher" to UserSwitcher, - ) - ) -} - -/** The main content of the app, that shows [GalleryAppScreens.Home] by default. */ -@Composable -private fun MainContent(onControlToggleRequested: () -> Unit) { - Box(Modifier.fillMaxSize()) { - val navController = rememberNavController() - NavHost( - navController = navController, - startDestination = GalleryAppScreens.Home.identifier, - ) { - screen(GalleryAppScreens.Home, navController, onControlToggleRequested) - } - } -} - -/** - * The top-level composable shown when starting the app. This composable always shows a - * [ConfigurationControls] at the top of the screen, above the [MainContent]. - */ -@Composable -fun GalleryApp( - theme: Theme, - onChangeTheme: () -> Unit, -) { - val systemFontScale = LocalDensity.current.fontScale - var fontScale: FontScale by rememberSaveable { - mutableStateOf( - FontScale.values().firstOrNull { it.scale == systemFontScale } ?: FontScale.Normal - ) - } - val context = LocalContext.current - val density = Density(context.resources.displayMetrics.density, fontScale.scale) - val onChangeFontScale = { - fontScale = - when (fontScale) { - FontScale.Small -> FontScale.Normal - FontScale.Normal -> FontScale.Big - FontScale.Big -> FontScale.Bigger - FontScale.Bigger -> FontScale.Small - } - } - - val systemLayoutDirection = LocalLayoutDirection.current - var layoutDirection by rememberSaveable { mutableStateOf(systemLayoutDirection) } - val onChangeLayoutDirection = { - layoutDirection = - when (layoutDirection) { - LayoutDirection.Ltr -> LayoutDirection.Rtl - LayoutDirection.Rtl -> LayoutDirection.Ltr - } - } - - CompositionLocalProvider( - LocalDensity provides density, - LocalLayoutDirection provides layoutDirection, - ) { - SystemUITheme { - Surface( - Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background, - ) { - Column(Modifier.fillMaxSize().systemBarsPadding()) { - var showControls by rememberSaveable { mutableStateOf(true) } - - if (showControls) { - ConfigurationControls( - theme, - fontScale, - layoutDirection, - onChangeTheme, - onChangeLayoutDirection, - onChangeFontScale, - Modifier.padding(horizontal = 16.dp), - ) - - Spacer(Modifier.height(4.dp)) - } - - MainContent(onControlToggleRequested = { showControls = !showControls }) - } - } - } - } -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/PeopleScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/PeopleScreen.kt deleted file mode 100644 index 2f0df7790ffd..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/PeopleScreen.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.compose.gallery - -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext -import com.android.systemui.people.emptyPeopleSpaceViewModel -import com.android.systemui.people.fewPeopleSpaceViewModel -import com.android.systemui.people.fullPeopleSpaceViewModel -import com.android.systemui.people.ui.compose.PeopleScreen -import com.android.systemui.people.ui.viewmodel.PeopleViewModel - -@Composable -fun EmptyPeopleScreen(onResult: (PeopleViewModel.Result) -> Unit) { - val context = LocalContext.current.applicationContext - val viewModel = emptyPeopleSpaceViewModel(context) - PeopleScreen(viewModel, onResult) -} - -@Composable -fun FewPeopleScreen(onResult: (PeopleViewModel.Result) -> Unit) { - val context = LocalContext.current.applicationContext - val viewModel = fewPeopleSpaceViewModel(context) - PeopleScreen(viewModel, onResult) -} - -@Composable -fun FullPeopleScreen(onResult: (PeopleViewModel.Result) -> Unit) { - val context = LocalContext.current.applicationContext - val viewModel = fullPeopleSpaceViewModel(context) - PeopleScreen(viewModel, onResult) -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt deleted file mode 100644 index d7d0d721b01c..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.compose.gallery - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.composable -import androidx.navigation.compose.navigation - -/** - * A screen in an app. It is either an [ParentScreen] which lists its child screens to navigate to - * them or a [ChildScreen] which shows some content. - */ -sealed class Screen(val identifier: String) - -class ParentScreen( - identifier: String, - val children: Map<String, Screen>, -) : Screen(identifier) - -class ChildScreen( - identifier: String, - val content: @Composable (NavController) -> Unit, -) : Screen(identifier) - -/** Create the navigation graph for [screen]. */ -fun NavGraphBuilder.screen( - screen: Screen, - navController: NavController, - onControlToggleRequested: () -> Unit, -) { - when (screen) { - is ChildScreen -> composable(screen.identifier) { screen.content(navController) } - is ParentScreen -> { - val menuRoute = "${screen.identifier}_menu" - navigation(startDestination = menuRoute, route = screen.identifier) { - // The menu to navigate to one of the children screens. - composable(menuRoute) { - ScreenMenu(screen, navController, onControlToggleRequested) - } - - // The content of the child screens. - screen.children.forEach { (_, child) -> - screen( - child, - navController, - onControlToggleRequested, - ) - } - } - } - } -} - -@Composable -private fun ScreenMenu( - screen: ParentScreen, - navController: NavController, - onControlToggleRequested: () -> Unit, -) { - LazyColumn( - Modifier.padding(horizontal = 16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - item { - Surface( - Modifier.fillMaxWidth(), - color = MaterialTheme.colorScheme.tertiaryContainer, - shape = CircleShape, - ) { - Column( - Modifier.clickable(onClick = onControlToggleRequested).padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text("Toggle controls") - } - } - } - - screen.children.forEach { (name, child) -> - item { - Surface( - Modifier.fillMaxWidth(), - color = MaterialTheme.colorScheme.secondaryContainer, - shape = CircleShape, - ) { - Column( - Modifier.clickable { navController.navigate(child.identifier) } - .padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text(name) - } - } - } - } - } -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/TypographyScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/TypographyScreen.kt deleted file mode 100644 index 147025ed1d60..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/TypographyScreen.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.compose.gallery - -import androidx.compose.foundation.horizontalScroll -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.style.TextOverflow - -/** The screen that shows the Material text styles. */ -@Composable -fun TypographyScreen() { - val typography = MaterialTheme.typography - - Column( - Modifier.fillMaxSize() - .horizontalScroll(rememberScrollState()) - .verticalScroll(rememberScrollState()), - ) { - FontLine("displayLarge", typography.displayLarge) - FontLine("displayMedium", typography.displayMedium) - FontLine("displaySmall", typography.displaySmall) - FontLine("headlineLarge", typography.headlineLarge) - FontLine("headlineMedium", typography.headlineMedium) - FontLine("headlineSmall", typography.headlineSmall) - FontLine("titleLarge", typography.titleLarge) - FontLine("titleMedium", typography.titleMedium) - FontLine("titleSmall", typography.titleSmall) - FontLine("bodyLarge", typography.bodyLarge) - FontLine("bodyMedium", typography.bodyMedium) - FontLine("bodySmall", typography.bodySmall) - FontLine("labelLarge", typography.labelLarge) - FontLine("labelMedium", typography.labelMedium) - FontLine("labelSmall", typography.labelSmall) - } -} - -@Composable -private fun FontLine(name: String, style: TextStyle) { - Text( - "$name (${style.fontSize}/${style.lineHeight}, W${style.fontWeight?.weight})", - style = style, - maxLines = 1, - overflow = TextOverflow.Visible, - ) -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/UserSwitcherScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/UserSwitcherScreen.kt deleted file mode 100644 index fe9707d22684..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/UserSwitcherScreen.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package com.android.systemui.compose.gallery - -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext -import com.android.systemui.user.Fakes.fakeUserSwitcherViewModel -import com.android.systemui.user.ui.compose.UserSwitcherScreen - -@Composable -fun UserSwitcherScreen( - userCount: Int, - onFinished: () -> Unit, -) { - val context = LocalContext.current.applicationContext - UserSwitcherScreen( - viewModel = fakeUserSwitcherViewModel(context, userCount = userCount), - onFinished = onFinished, - ) -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/people/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/people/Fakes.kt deleted file mode 100644 index 0966c3233ad5..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/people/Fakes.kt +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.people - -import android.content.Context -import android.graphics.Bitmap -import android.graphics.Canvas -import android.graphics.Color -import android.graphics.Paint -import android.graphics.drawable.Icon -import androidx.core.graphics.drawable.toIcon -import com.android.systemui.R -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.people.data.model.PeopleTileModel -import com.android.systemui.people.ui.viewmodel.PeopleViewModel -import com.android.systemui.people.widget.PeopleTileKey - -/** A [PeopleViewModel] that does not have any conversations. */ -fun emptyPeopleSpaceViewModel(@Application context: Context): PeopleViewModel { - return fakePeopleSpaceViewModel(context, emptyList(), emptyList()) -} - -/** A [PeopleViewModel] that has a few conversations. */ -fun fewPeopleSpaceViewModel(@Application context: Context): PeopleViewModel { - return fakePeopleSpaceViewModel( - context, - priorityTiles = - listOf( - fakeTile(context, id = "0", Color.RED, "Priority"), - fakeTile(context, id = "1", Color.BLUE, "Priority NewStory", hasNewStory = true), - ), - recentTiles = - listOf( - fakeTile(context, id = "2", Color.GREEN, "Recent Important", isImportant = true), - fakeTile(context, id = "3", Color.CYAN, "Recent DndBlocking", isDndBlocking = true), - ), - ) -} - -/** A [PeopleViewModel] that has a lot of conversations. */ -fun fullPeopleSpaceViewModel(@Application context: Context): PeopleViewModel { - return fakePeopleSpaceViewModel( - context, - priorityTiles = - listOf( - fakeTile(context, id = "0", Color.RED, "Priority"), - fakeTile(context, id = "1", Color.BLUE, "Priority NewStory", hasNewStory = true), - fakeTile(context, id = "2", Color.GREEN, "Priority Important", isImportant = true), - fakeTile( - context, - id = "3", - Color.CYAN, - "Priority DndBlocking", - isDndBlocking = true, - ), - fakeTile( - context, - id = "4", - Color.MAGENTA, - "Priority NewStory Important", - hasNewStory = true, - isImportant = true, - ), - ), - recentTiles = - listOf( - fakeTile( - context, - id = "5", - Color.RED, - "Recent NewStory DndBlocking", - hasNewStory = true, - isDndBlocking = true, - ), - fakeTile( - context, - id = "6", - Color.BLUE, - "Recent Important DndBlocking", - isImportant = true, - isDndBlocking = true, - ), - fakeTile( - context, - id = "7", - Color.GREEN, - "Recent NewStory Important DndBlocking", - hasNewStory = true, - isImportant = true, - isDndBlocking = true, - ), - fakeTile(context, id = "8", Color.CYAN, "Recent"), - fakeTile(context, id = "9", Color.MAGENTA, "Recent"), - ), - ) -} - -private fun fakePeopleSpaceViewModel( - @Application context: Context, - priorityTiles: List<PeopleTileModel>, - recentTiles: List<PeopleTileModel>, -): PeopleViewModel { - return PeopleViewModel( - context, - FakePeopleTileRepository(priorityTiles, recentTiles), - FakePeopleWidgetRepository(), - ) -} - -private fun fakeTile( - @Application context: Context, - id: String, - iconColor: Int, - username: String, - hasNewStory: Boolean = false, - isImportant: Boolean = false, - isDndBlocking: Boolean = false -): PeopleTileModel { - return PeopleTileModel( - PeopleTileKey(id, /* userId= */ 0, /* packageName */ ""), - username, - fakeUserIcon(context, iconColor), - hasNewStory, - isImportant, - isDndBlocking, - ) -} - -private fun fakeUserIcon(@Application context: Context, color: Int): Icon { - val size = context.resources.getDimensionPixelSize(R.dimen.avatar_size_for_medium) - val bitmap = - Bitmap.createBitmap( - size, - size, - Bitmap.Config.ARGB_8888, - ) - val canvas = Canvas(bitmap) - val paint = Paint().apply { this.color = color } - val radius = size / 2f - canvas.drawCircle(/* cx= */ radius, /* cy= */ radius, /* radius= */ radius, paint) - return bitmap.toIcon() -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt deleted file mode 100644 index 6588e22721fb..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.qs.footer - -import android.content.Context -import android.os.UserHandle -import android.view.View -import com.android.internal.util.UserIcons -import com.android.systemui.R -import com.android.systemui.animation.Expandable -import com.android.systemui.classifier.FalsingManagerFake -import com.android.systemui.common.shared.model.Icon -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.globalactions.GlobalActionsDialogLite -import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel -import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor -import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig -import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel -import com.android.systemui.util.mockito.mock -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf - -/** A list of fake [FooterActionsViewModel] to be used in screenshot tests and the gallery. */ -fun fakeFooterActionsViewModels( - @Application context: Context, -): List<FooterActionsViewModel> { - return listOf( - fakeFooterActionsViewModel(context), - fakeFooterActionsViewModel(context, showPowerButton = false, isGuestUser = true), - fakeFooterActionsViewModel(context, showUserSwitcher = false), - fakeFooterActionsViewModel(context, showUserSwitcher = false, foregroundServices = 4), - fakeFooterActionsViewModel( - context, - foregroundServices = 4, - hasNewForegroundServices = true, - userId = 1, - ), - fakeFooterActionsViewModel( - context, - securityText = "Security", - foregroundServices = 4, - showUserSwitcher = false, - ), - fakeFooterActionsViewModel( - context, - securityText = "Security (not clickable)", - securityClickable = false, - foregroundServices = 4, - hasNewForegroundServices = true, - userId = 2, - ), - ) -} - -private fun fakeFooterActionsViewModel( - @Application context: Context, - securityText: String? = null, - securityClickable: Boolean = true, - foregroundServices: Int = 0, - hasNewForegroundServices: Boolean = false, - showUserSwitcher: Boolean = true, - showPowerButton: Boolean = true, - userId: Int = UserHandle.USER_OWNER, - isGuestUser: Boolean = false, -): FooterActionsViewModel { - val interactor = - FakeFooterActionsInteractor( - securityButtonConfig = - flowOf( - securityText?.let { text -> - SecurityButtonConfig( - icon = - Icon.Resource( - R.drawable.ic_info_outline, - contentDescription = null, - ), - text = text, - isClickable = securityClickable, - ) - } - ), - foregroundServicesCount = flowOf(foregroundServices), - hasNewForegroundServices = flowOf(hasNewForegroundServices), - userSwitcherStatus = - flowOf( - if (showUserSwitcher) { - UserSwitcherStatusModel.Enabled( - currentUserName = "foo", - currentUserImage = - UserIcons.getDefaultUserIcon( - context.resources, - userId, - /* light= */ false, - ), - isGuestUser = isGuestUser, - ) - } else { - UserSwitcherStatusModel.Disabled - } - ), - deviceMonitoringDialogRequests = flowOf(), - ) - - return FooterActionsViewModel( - context, - interactor, - FalsingManagerFake(), - globalActionsDialogLite = mock(), - showPowerButton = showPowerButton, - ) -} - -private class FakeFooterActionsInteractor( - override val securityButtonConfig: Flow<SecurityButtonConfig?> = flowOf(null), - override val foregroundServicesCount: Flow<Int> = flowOf(0), - override val hasNewForegroundServices: Flow<Boolean> = flowOf(false), - override val userSwitcherStatus: Flow<UserSwitcherStatusModel> = - flowOf(UserSwitcherStatusModel.Disabled), - override val deviceMonitoringDialogRequests: Flow<Unit> = flowOf(), - private val onShowDeviceMonitoringDialogFromView: (View) -> Unit = {}, - private val onShowDeviceMonitoringDialog: (Context) -> Unit = {}, - private val onShowForegroundServicesDialog: (View) -> Unit = {}, - private val onShowPowerMenuDialog: (GlobalActionsDialogLite, View) -> Unit = { _, _ -> }, - private val onShowSettings: (Expandable) -> Unit = {}, - private val onShowUserSwitcher: (View) -> Unit = {}, -) : FooterActionsInteractor { - override fun showDeviceMonitoringDialog(view: View) { - onShowDeviceMonitoringDialogFromView(view) - } - - override fun showDeviceMonitoringDialog(quickSettingsContext: Context) { - onShowDeviceMonitoringDialog(quickSettingsContext) - } - - override fun showForegroundServicesDialog(view: View) { - onShowForegroundServicesDialog(view) - } - - override fun showPowerMenuDialog(globalActionsDialogLite: GlobalActionsDialogLite, view: View) { - onShowPowerMenuDialog(globalActionsDialogLite, view) - } - - override fun showSettings(expandable: Expandable) { - onShowSettings(expandable) - } - - override fun showUserSwitcher(view: View) { - onShowUserSwitcher(view) - } -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt deleted file mode 100644 index 91a73ea16dc4..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package com.android.systemui.user - -import android.content.Context -import androidx.appcompat.content.res.AppCompatResources -import com.android.systemui.common.shared.model.Text -import com.android.systemui.compose.gallery.R -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor -import com.android.systemui.power.data.repository.FakePowerRepository -import com.android.systemui.power.domain.interactor.PowerInteractor -import com.android.systemui.user.data.repository.FakeUserRepository -import com.android.systemui.user.domain.interactor.UserInteractor -import com.android.systemui.user.shared.model.UserActionModel -import com.android.systemui.user.shared.model.UserModel -import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel -import com.android.systemui.util.mockito.mock - -object Fakes { - private val USER_TINT_COLORS = - arrayOf( - 0x000000, - 0x0000ff, - 0x00ff00, - 0x00ffff, - 0xff0000, - 0xff00ff, - 0xffff00, - 0xffffff, - ) - - fun fakeUserSwitcherViewModel( - context: Context, - userCount: Int, - ): UserSwitcherViewModel { - return UserSwitcherViewModel.Factory( - userInteractor = - UserInteractor( - repository = - FakeUserRepository().apply { - setUsers( - (0 until userCount).map { index -> - UserModel( - id = index, - name = - Text.Loaded( - when (index % 6) { - 0 -> "Ross Geller" - 1 -> "Phoebe Buffay" - 2 -> "Monica Geller" - 3 -> "Rachel Greene" - 4 -> "Chandler Bing" - else -> "Joey Tribbiani" - } - ), - image = - checkNotNull( - AppCompatResources.getDrawable( - context, - when (index % 6) { - 0 -> R.drawable.kitten1 - 1 -> R.drawable.kitten2 - 2 -> R.drawable.kitten3 - 3 -> R.drawable.kitten4 - 4 -> R.drawable.kitten5 - else -> R.drawable.kitten6 - }, - ) - ), - isSelected = index == 0, - isSelectable = true, - ) - } - ) - setActions( - UserActionModel.values().mapNotNull { - if (it == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) { - null - } else { - it - } - } - ) - }, - controller = mock(), - activityStarter = mock(), - keyguardInteractor = - KeyguardInteractor( - repository = - FakeKeyguardRepository().apply { setKeyguardShowing(false) }, - ), - ), - powerInteractor = - PowerInteractor( - repository = FakePowerRepository(), - ) - ) - .create(UserSwitcherViewModel::class.java) - } -} diff --git a/packages/SystemUI/compose/gallery/tests/Android.bp b/packages/SystemUI/compose/gallery/tests/Android.bp deleted file mode 100644 index 3e01f7d2c431..000000000000 --- a/packages/SystemUI/compose/gallery/tests/Android.bp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2022 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], -} - -android_test { - name: "SystemUIComposeGalleryTests", - manifest: "AndroidManifest.xml", - test_suites: ["device-tests"], - sdk_version: "current", - certificate: "platform", - - srcs: [ - "src/**/*.kt", - ], - - static_libs: [ - "SystemUIComposeGalleryLib", - - "androidx.test.runner", - "androidx.test.ext.junit", - - "androidx.compose.runtime_runtime", - "androidx.compose.ui_ui-test-junit4", - "androidx.compose.ui_ui-test-manifest", - ], - - kotlincflags: ["-Xjvm-default=enable"], -} diff --git a/packages/SystemUI/compose/gallery/tests/AndroidManifest.xml b/packages/SystemUI/compose/gallery/tests/AndroidManifest.xml deleted file mode 100644 index 5eeb3ad24e5a..000000000000 --- a/packages/SystemUI/compose/gallery/tests/AndroidManifest.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2022 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. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.systemui.compose.gallery.tests" > - - <application> - <uses-library android:name="android.test.runner" /> - </application> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.systemui.compose.gallery.tests" - android:label="Tests for SystemUIComposeGallery"/> - -</manifest>
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/tests/src/com/android/systemui/compose/gallery/ScreenshotsTests.kt b/packages/SystemUI/compose/gallery/tests/src/com/android/systemui/compose/gallery/ScreenshotsTests.kt deleted file mode 100644 index 66ecc8d4fde5..000000000000 --- a/packages/SystemUI/compose/gallery/tests/src/com/android/systemui/compose/gallery/ScreenshotsTests.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.compose.gallery - -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.systemui.compose.theme.SystemUITheme -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class ScreenshotsTests { - @get:Rule val composeRule = createComposeRule() - - @Test - fun exampleFeatureScreenshotTest() { - // TODO(b/230832101): Wire this with the screenshot diff testing infra. We should reuse the - // configuration of the features in the gallery app to populate the UIs. - composeRule.setContent { SystemUITheme { ExampleFeatureScreen() } } - } -} diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt index 818248439d0a..9f275afa3765 100644 --- a/packages/SystemUI/ktfmt_includes.txt +++ b/packages/SystemUI/ktfmt_includes.txt @@ -813,7 +813,7 @@ -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManagerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt +-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt @@ -828,7 +828,7 @@ -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml index ae8680a096b4..5ee67d917768 100644 --- a/packages/SystemUI/res-keyguard/values-ro/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml @@ -37,7 +37,7 @@ <string name="keyguard_missing_sim_instructions" msgid="1162120926141335918">"Introdu un card SIM."</string> <string name="keyguard_missing_sim_instructions_long" msgid="2712623293749378570">"Cardul SIM lipsește sau nu poate fi citit. Introdu un card SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="5842745213110966962">"Card SIM inutilizabil."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="2490584154727897806">"Cardul dvs. SIM este dezactivat definitiv.\n Contactați furnizorul de servicii wireless pentru a obține un alt card SIM."</string> + <string name="keyguard_permanent_disabled_sim_instructions" msgid="2490584154727897806">"Cardul SIM e dezactivat definitiv.\n Contactează furnizorul de servicii wireless pentru a obține un alt card SIM."</string> <string name="keyguard_sim_locked_message" msgid="4343544458476911044">"Cardul SIM este blocat."</string> <string name="keyguard_sim_puk_locked_message" msgid="6253830777745450550">"Cardul SIM este blocat cu codul PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="2394023844117630429">"Se deblochează cardul SIM…"</string> @@ -46,7 +46,7 @@ <string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"Zona codului PIN pentru cardul SIM"</string> <string name="keyguard_accessibility_sim_puk_area" msgid="5537294043180237374">"Zona codului PUK pentru cardul SIM"</string> <string name="keyboardview_keycode_delete" msgid="8489719929424895174">"Șterge"</string> - <string name="disable_carrier_button_text" msgid="7153361131709275746">"Dezactivați cardul eSIM"</string> + <string name="disable_carrier_button_text" msgid="7153361131709275746">"Dezactivează cardul eSIM"</string> <string name="error_disable_esim_title" msgid="3802652622784813119">"Nu se poate dezactiva cardul eSIM"</string> <string name="error_disable_esim_msg" msgid="2441188596467999327">"Cardul eSIM nu poate fi dezactivat din cauza unei erori."</string> <string name="keyboardview_keycode_enter" msgid="6727192265631761174">"Introdu"</string> @@ -56,24 +56,24 @@ <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Reîncearcă peste o secundă.}few{Reîncearcă peste # secunde.}other{Reîncearcă peste # de secunde.}}"</string> <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Introdu codul PIN al cardului SIM."</string> <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Introdu codul PIN al cardului SIM pentru „<xliff:g id="CARRIER">%1$s</xliff:g>”."</string> - <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Dezactivați cardul eSIM pentru a folosi dispozitivul fără serviciu mobil."</string> - <string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"Cardul SIM este acum dezactivat. Pentru a continua, introduceți codul PUK. Pentru detalii, contactați operatorul."</string> - <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"Cardul SIM „<xliff:g id="CARRIER">%1$s</xliff:g>\" este acum dezactivat. Pentru a continua, introduceți codul PUK. Pentru detalii, contactați operatorul."</string> + <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Dezactivează cardul eSIM pentru a folosi dispozitivul fără serviciu mobil."</string> + <string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"Cardul SIM e acum dezactivat. Pentru a continua, introdu codul PUK. Pentru detalii, contactează operatorul."</string> + <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"Cardul SIM „<xliff:g id="CARRIER">%1$s</xliff:g>\" e acum dezactivat. Pentru a continua, introdu codul PUK. Pentru detalii, contactează operatorul."</string> <string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"Introdu codul PIN dorit"</string> - <string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Confirmați codul PIN dorit"</string> + <string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Confirmă codul PIN dorit"</string> <string name="kg_sim_unlock_progress_dialog_message" msgid="4251352015304070326">"Se deblochează cardul SIM…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Introdu un cod PIN alcătuit din 4 până la 8 cifre."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"Codul PUK trebuie să aibă minimum 8 cifre."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Ați introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> - <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Ați introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> - <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> - <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Codul PIN pentru cardul SIM este incorect. Contactați operatorul pentru a vă debloca dispozitivul."</string> - <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Codul PIN pentru cardul SIM este incorect. V-a mai rămas # încercare, după care va trebui să contactați operatorul pentru a vă debloca dispozitivul.}few{Codul PIN pentru cardul SIM este incorect. V-au mai rămas # încercări. }other{Codul PIN pentru cardul SIM este incorect. V-au mai rămas # de încercări. }}"</string> - <string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Cardul SIM nu poate fi utilizat. Contactați operatorul."</string> - <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Codul PUK pentru cardul SIM este incorect. V-a mai rămas # încercare până când cardul SIM va deveni inutilizabil definitiv.}few{Codul PUK pentru cardul SIM este incorect. V-au mai rămas # încercări până când cardul SIM va deveni inutilizabil definitiv.}other{Codul PUK pentru cardul SIM este incorect. V-au mai rămas # de încercări până când cardul SIM va deveni inutilizabil definitiv.}}"</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Ai introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> + <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Ai introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> + <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string> + <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Codul PIN pentru cardul SIM este incorect. Contactează operatorul pentru a debloca dispozitivul."</string> + <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{PIN-ul cardului SIM e incorect. Ți-a mai rămas # încercare, după care va trebui să contactezi operatorul pentru a debloca dispozitivul.}few{PIN-ul cardului SIM e incorect. Ți-au mai rămas # încercări. }other{PIN-ul cardului SIM e incorect. Ți-au mai rămas # de încercări. }}"</string> + <string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Cardul SIM nu poate fi utilizat. Contactează operatorul."</string> + <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Codul PUK pentru cardul SIM e incorect. Ți-a mai rămas # încercare până când cardul SIM va deveni inutilizabil definitiv.}few{Codul PUK pentru cardul SIM e incorect. Ți-au mai rămas # încercări până când cardul SIM va deveni inutilizabil definitiv.}other{Codul PUK pentru cardul SIM e incorect. Ți-au mai rămas # de încercări până când cardul SIM va deveni inutilizabil definitiv.}}"</string> <string name="kg_password_pin_failed" msgid="5136259126330604009">"Deblocarea cu ajutorul codului PIN pentru cardul SIM nu a reușit!"</string> <string name="kg_password_puk_failed" msgid="6778867411556937118">"Deblocarea cu ajutorul codului PUK pentru cardul SIM nu a reușit!"</string> - <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Comutați metoda de introducere"</string> + <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Schimbă metoda de introducere"</string> <string name="airplane_mode" msgid="2528005343938497866">"Mod Avion"</string> <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Modelul este necesar după repornirea dispozitivului"</string> <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Codul PIN este necesar după repornirea dispozitivului"</string> @@ -84,9 +84,9 @@ <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Dispozitiv blocat de administrator"</string> <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Dispozitivul a fost blocat manual"</string> <string name="kg_face_not_recognized" msgid="7903950626744419160">"Nu este recunoscut"</string> - <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Pentru Deblocare facială, activați accesul la cameră"</string> - <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Introduceți codul PIN pentru cardul SIM. V-a mai rămas # încercare, după care va trebui să contactați operatorul pentru a vă debloca dispozitivul.}few{Introduceți codul PIN al cardului SIM. V-au rămas # încercări.}other{Introduceți codul PIN al cardului SIM. V-au rămas # de încercări.}}"</string> - <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Cardul SIM este dezactivat acum. Introduceți codul PUK pentru a continua. V-a mai rămas # încercare până când cardul SIM va deveni inutilizabil definitiv. Contactați operatorul pentru detalii.}few{Cardul SIM este dezactivat acum. Introduceți codul PUK pentru a continua. V-au mai rămas # încercări până când cardul SIM va deveni inutilizabil definitiv. Contactați operatorul pentru detalii.}other{Cardul SIM este dezactivat acum. Introduceți codul PUK pentru a continua. V-au mai rămas # de încercări până când cardul SIM va deveni inutilizabil definitiv. Contactați operatorul pentru detalii.}}"</string> + <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Pentru Deblocare facială, activează accesul la cameră"</string> + <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Introdu PIN-ul cardului SIM. Ți-a mai rămas # încercare, după care va trebui să contactezi operatorul pentru a debloca dispozitivul.}few{Introdu PIN-ul cardului SIM. Ți-au rămas # încercări.}other{Introdu PIN-ul cardului SIM. Ți-au rămas # de încercări.}}"</string> + <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Cardul SIM e acum dezactivat. Introdu codul PUK pentru a continua. Ți-a mai rămas # încercare până când cardul SIM va deveni inutilizabil definitiv. Contactează operatorul pentru detalii.}few{Cardul SIM e acum dezactivat. Introdu codul PUK pentru a continua. Ți-au mai rămas # încercări până când cardul SIM va deveni inutilizabil definitiv. Contactează operatorul pentru detalii.}other{Cardul SIM e acum dezactivat. Introdu codul PUK pentru a continua. Ți-au mai rămas # de încercări până când cardul SIM va deveni inutilizabil definitiv. Contactează operatorul pentru detalii.}}"</string> <string name="clock_title_default" msgid="6342735240617459864">"Prestabilit"</string> <string name="clock_title_bubble" msgid="2204559396790593213">"Balon"</string> <string name="clock_title_analog" msgid="8409262532900918273">"Analogic"</string> diff --git a/packages/SystemUI/res-product/values-ro/strings.xml b/packages/SystemUI/res-product/values-ro/strings.xml index b260f28d8f03..471f01e678e1 100644 --- a/packages/SystemUI/res-product/values-ro/strings.xml +++ b/packages/SystemUI/res-product/values-ro/strings.xml @@ -19,30 +19,30 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dock_alignment_slow_charging" product="default" msgid="6997633396534416792">"Repoziționați telefonul pentru încărcare mai rapidă"</string> - <string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"Repoziționați telefonul pentru încărcarea wireless"</string> + <string name="dock_alignment_slow_charging" product="default" msgid="6997633396534416792">"Repoziționează telefonul pentru încărcare mai rapidă"</string> + <string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"Repoziționează telefonul pentru încărcarea wireless"</string> <string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"Dispozitivul Android TV se va opri în curând. Apasă un buton pentru a-l menține pornit."</string> <string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"Dispozitivul se va opri în curând. Apasă pentru a-l menține pornit."</string> <string name="keyguard_missing_sim_message" product="tablet" msgid="5018086454277963787">"Nu există card SIM în tabletă."</string> <string name="keyguard_missing_sim_message" product="default" msgid="7053347843877341391">"Nu există card SIM în telefon."</string> <string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"Codurile PIN nu coincid"</string> - <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, această tabletă va fi resetată, iar toate datele acesteia vor fi șterse."</string> - <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acest telefon va fi resetat, iar toate datele acestuia vor fi șterse."</string> - <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="8710104080409538587">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Această tabletă va fi resetată, iar toate datele acesteia vor fi șterse."</string> - <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Acest telefon va fi resetat, iar toate datele acestuia vor fi șterse."</string> - <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="7325071812832605911">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string> - <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string> - <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string> - <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string> - <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string> - <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string> - <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string> - <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string> - <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați tableta cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> - <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> - <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Deblocați telefonul pentru mai multe opțiuni"</string> - <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Deblocați tableta pentru mai multe opțiuni"</string> - <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Deblocați dispozitivul pentru mai multe opțiuni"</string> + <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, tableta va fi resetată, iar toate datele vor fi șterse."</string> + <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acest telefon va fi resetat, iar toate datele acestuia vor fi șterse."</string> + <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="8710104080409538587">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Această tabletă va fi resetată, iar toate datele vor fi șterse."</string> + <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Acest telefon va fi resetat, iar toate datele acestuia vor fi șterse."</string> + <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="7325071812832605911">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string> + <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string> + <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string> + <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string> + <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string> + <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string> + <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string> + <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string> + <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, ți se va solicita să deblochezi tableta cu ajutorul unui cont de e-mail.\n\n Încearcă din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> + <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, ți se va solicita să deblochezi telefonul cu ajutorul unui cont de e-mail.\n\n Încearcă din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string> + <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Deblochează telefonul pentru mai multe opțiuni"</string> + <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Deblochează tableta pentru mai multe opțiuni"</string> + <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Deblochează dispozitivul pentru mai multe opțiuni"</string> <string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Se redă pe acest telefon"</string> <string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Se redă pe această tabletă"</string> </resources> diff --git a/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml b/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml index 420ae7044149..e015ed3d0c09 100644 --- a/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml +++ b/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml @@ -32,23 +32,6 @@ </set> </aapt:attr> </target> - <target android:name="_R_G_L_0_G_D_0_P_1"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator - android:duration="167" - android:propertyName="fillColor" - android:startOffset="0" - android:valueFrom="#000000" - android:valueTo="#ffffff" - android:valueType="colorType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> <target android:name="time_group"> <aapt:attr name="android:animation"> <set android:ordering="together"> @@ -72,29 +55,13 @@ <group android:name="_R_G_L_0_G" android:translateX="12" - android:translateY="12.469"> - <group - android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0" - android:scaleX="1" - android:scaleY="1"> - <path - android:name="_R_G_L_0_G_D_0_P_0" - android:fillAlpha="1" - android:fillColor="#000000" - android:fillType="nonZero" - android:pathData=" M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c " /> - </group> - <group - android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0" - android:scaleX="1" - android:scaleY="1"> - <path - android:name="_R_G_L_0_G_D_0_P_1" - android:fillAlpha="1" - android:fillColor="#000000" - android:fillType="nonZero" - android:pathData=" M1.51 1.5 C1.51,1.5 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.5 1.51,1.5c " /> - </group> + android:translateY="11.969"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#000000" + android:fillType="nonZero" + android:pathData=" M-3.5 8.5 C-3.5,8.5 -3.5,10 -3.5,10 C-3.5,10 0,9 0,9 C0,9 3.5,10 3.5,10 C3.5,10 3.5,8.5 3.5,8.5 C3.5,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5 C1.5,1.5 10,4 10,4 C10,4 10,2 10,2 C10,2 1.5,-3 1.5,-3 C1.5,-3 1.5,-8.02 1.5,-8.02 C1.5,-8.44 1.2,-9.52 0,-9.52 C-1.19,-9.52 -1.5,-8.44 -1.5,-8.02 C-1.5,-8.02 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -3.5,8.5 -3.5,8.5c " /> </group> </group> <group android:name="time_group" /> diff --git a/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml b/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml index 57d7902f59ca..a44a9b6fbf23 100644 --- a/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml +++ b/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml @@ -58,170 +58,75 @@ </aapt:attr> </objectAnimator> <objectAnimator - android:duration="767" + android:duration="67" android:propertyName="scaleX" android:startOffset="383" android:valueFrom="1.1500000000000001" - android:valueTo="1" + android:valueTo="1.1500000000000001" android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.199,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.4,1 1.0,1.0" /> </aapt:attr> </objectAnimator> <objectAnimator - android:duration="767" + android:duration="67" android:propertyName="scaleY" android:startOffset="383" android:valueFrom="1.1500000000000001" - android:valueTo="1" - android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.199,1 1.0,1.0" /> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_D_0_P_0"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator - android:duration="200" - android:propertyName="pathData" - android:startOffset="0" - android:valueFrom="M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c " - android:valueTo="M-1.74 -2.93 C-1.74,-2.93 -2.06,-7.3 -2.06,-7.3 C-2.14,-8.99 -1.15,-10 0,-10 C1.15,-10 2.08,-8.88 2.09,-7.3 C2.09,-7.3 1.74,-3.09 1.74,-3.09 C1.74,-3.09 9.44,2.79 9.44,2.79 C9.44,2.79 9.44,4.79 9.44,4.79 C9.44,4.79 1.69,1.57 1.69,1.57 C1.69,1.57 -1.74,-2.93 -1.74,-2.93c " - android:valueType="pathType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.833 1.0,1.0" /> - </aapt:attr> - </objectAnimator> - <objectAnimator - android:duration="217" - android:propertyName="pathData" - android:startOffset="200" - android:valueFrom="M-1.74 -2.93 C-1.74,-2.93 -2.06,-7.3 -2.06,-7.3 C-2.14,-8.99 -1.15,-10 0,-10 C1.15,-10 2.08,-8.88 2.09,-7.3 C2.09,-7.3 1.74,-3.09 1.74,-3.09 C1.74,-3.09 9.44,2.79 9.44,2.79 C9.44,2.79 9.44,4.79 9.44,4.79 C9.44,4.79 1.69,1.57 1.69,1.57 C1.69,1.57 -1.74,-2.93 -1.74,-2.93c " - android:valueTo="M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c " - android:valueType="pathType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> - </aapt:attr> - </objectAnimator> - <objectAnimator - android:duration="217" - android:propertyName="pathData" - android:startOffset="417" - android:valueFrom="M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c " - android:valueTo="M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c " - android:valueType="pathType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" /> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_D_0_P_1"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator - android:duration="250" - android:propertyName="fillColor" - android:startOffset="0" - android:valueFrom="#ffffff" - android:valueTo="#000000" - android:valueType="colorType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator - android:duration="383" - android:propertyName="scaleX" - android:startOffset="0" - android:valueFrom="1" android:valueTo="1.1500000000000001" android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.4,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.4,1 1.0,1.0" /> </aapt:attr> </objectAnimator> <objectAnimator - android:duration="383" - android:propertyName="scaleY" - android:startOffset="0" - android:valueFrom="1" - android:valueTo="1.1500000000000001" - android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.4,1 1.0,1.0" /> - </aapt:attr> - </objectAnimator> - <objectAnimator - android:duration="767" + android:duration="700" android:propertyName="scaleX" - android:startOffset="383" + android:startOffset="450" android:valueFrom="1.1500000000000001" android:valueTo="1" android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.199,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.4,1 1.0,1.0" /> </aapt:attr> </objectAnimator> <objectAnimator - android:duration="767" + android:duration="700" android:propertyName="scaleY" - android:startOffset="383" + android:startOffset="450" android:valueFrom="1.1500000000000001" android:valueTo="1" android:valueType="floatType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.199,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.4,1 1.0,1.0" /> </aapt:attr> </objectAnimator> </set> </aapt:attr> </target> - <target android:name="_R_G_L_0_G_D_0_P_1"> + <target android:name="_R_G_L_0_G_D_0_P_0"> <aapt:attr name="android:animation"> <set android:ordering="together"> <objectAnimator android:duration="200" android:propertyName="pathData" android:startOffset="0" - android:valueFrom="M1.51 1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.49 1.51,1.49c " - android:valueTo="M1.69 1.58 C1.69,1.58 -1.74,-2.92 -1.74,-2.92 C-1.74,-2.92 -9.44,2.79 -9.44,2.79 C-9.44,2.79 -9.44,4.79 -9.44,4.79 C-9.44,4.79 -1.67,1.42 -1.67,1.42 C-1.67,1.42 -1.11,7.28 -1.11,7.28 C-1.11,7.28 -2.94,8.78 -2.94,8.78 C-2.94,8.78 -2.92,10 -2.92,10 C-2.92,10 0,9 0,9 C0,9 2.92,10 2.92,10 C2.92,10 2.91,8.78 2.91,8.78 C2.91,8.78 1.08,7.28 1.08,7.28 C1.08,7.28 1.69,1.58 1.69,1.58c " + android:valueFrom="M-3.5 8.5 C-3.5,8.5 -3.5,10 -3.5,10 C-3.5,10 0,9 0,9 C0,9 3.5,10 3.5,10 C3.5,10 3.5,8.5 3.5,8.5 C3.5,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5 C1.5,1.5 10,4 10,4 C10,4 10,2 10,2 C10,2 1.5,-3 1.5,-3 C1.5,-3 1.5,-8.02 1.5,-8.02 C1.5,-8.44 1.2,-9.52 0,-9.52 C-1.19,-9.52 -1.5,-8.44 -1.5,-8.02 C-1.5,-8.02 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -3.5,8.5 -3.5,8.5c " + android:valueTo="M-3.23 8.38 C-3.23,8.38 -3.23,9.4 -3.23,9.4 C-3.23,9.4 0,8.51 0,8.51 C0,8.51 3.23,9.4 3.23,9.4 C3.23,9.4 3.23,8.38 3.23,8.38 C3.23,8.38 1.26,6.51 1.26,6.51 C1.26,6.51 1.59,1.5 1.59,1.5 C1.59,1.5 10,3.66 10,3.66 C10,3.66 10,1.95 10,1.95 C10,1.95 1.59,-3.36 1.59,-3.36 C1.59,-3.36 1.77,-8.02 1.77,-8.02 C1.77,-8.44 1.2,-9.52 0,-9.52 C-1.19,-9.52 -1.77,-8.44 -1.77,-8.02 C-1.77,-8.02 -1.59,-3.36 -1.59,-3.36 C-1.59,-3.36 -10,1.95 -10,1.95 C-10,1.95 -10,3.66 -10,3.66 C-10,3.66 -1.59,1.5 -1.59,1.5 C-1.59,1.5 -1.24,6.51 -1.24,6.51 C-1.24,6.51 -3.23,8.38 -3.23,8.38c " android:valueType="pathType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.833 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> </aapt:attr> </objectAnimator> <objectAnimator - android:duration="217" + android:duration="383" android:propertyName="pathData" android:startOffset="200" - android:valueFrom="M1.69 1.58 C1.69,1.58 -1.74,-2.92 -1.74,-2.92 C-1.74,-2.92 -9.44,2.79 -9.44,2.79 C-9.44,2.79 -9.44,4.79 -9.44,4.79 C-9.44,4.79 -1.67,1.42 -1.67,1.42 C-1.67,1.42 -1.11,7.28 -1.11,7.28 C-1.11,7.28 -2.94,8.78 -2.94,8.78 C-2.94,8.78 -2.92,10 -2.92,10 C-2.92,10 0,9 0,9 C0,9 2.92,10 2.92,10 C2.92,10 2.91,8.78 2.91,8.78 C2.91,8.78 1.08,7.28 1.08,7.28 C1.08,7.28 1.69,1.58 1.69,1.58c " - android:valueTo="M1.51 1.5 C1.51,1.5 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.5 1.51,1.5c " + android:valueFrom="M-3.23 8.38 C-3.23,8.38 -3.23,9.4 -3.23,9.4 C-3.23,9.4 0,8.51 0,8.51 C0,8.51 3.23,9.4 3.23,9.4 C3.23,9.4 3.23,8.38 3.23,8.38 C3.23,8.38 1.26,6.51 1.26,6.51 C1.26,6.51 1.59,1.5 1.59,1.5 C1.59,1.5 10,3.66 10,3.66 C10,3.66 10,1.95 10,1.95 C10,1.95 1.59,-3.36 1.59,-3.36 C1.59,-3.36 1.77,-8.02 1.77,-8.02 C1.77,-8.44 1.2,-9.52 0,-9.52 C-1.19,-9.52 -1.77,-8.44 -1.77,-8.02 C-1.77,-8.02 -1.59,-3.36 -1.59,-3.36 C-1.59,-3.36 -10,1.95 -10,1.95 C-10,1.95 -10,3.66 -10,3.66 C-10,3.66 -1.59,1.5 -1.59,1.5 C-1.59,1.5 -1.24,6.51 -1.24,6.51 C-1.24,6.51 -3.23,8.38 -3.23,8.38c " + android:valueTo="M-3.5 8.5 C-3.5,8.5 -3.5,10 -3.5,10 C-3.5,10 0,9 0,9 C0,9 3.5,10 3.5,10 C3.5,10 3.5,8.5 3.5,8.5 C3.5,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5 C1.5,1.5 10,4 10,4 C10,4 10,2 10,2 C10,2 1.5,-3 1.5,-3 C1.5,-3 1.5,-8.02 1.5,-8.02 C1.5,-8.44 1.2,-9.52 0,-9.52 C-1.19,-9.52 -1.5,-8.44 -1.5,-8.02 C-1.5,-8.02 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -3.5,8.5 -3.5,8.5c " android:valueType="pathType"> <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> - </aapt:attr> - </objectAnimator> - <objectAnimator - android:duration="217" - android:propertyName="pathData" - android:startOffset="417" - android:valueFrom="M1.51 1.5 C1.51,1.5 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.5 1.51,1.5c " - android:valueTo="M1.51 1.5 C1.51,1.5 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.5 1.51,1.5c " - android:valueType="pathType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" /> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.833 1.0,1.0" /> </aapt:attr> </objectAnimator> </set> @@ -250,7 +155,7 @@ <group android:name="_R_G_L_0_G" android:translateX="12" - android:translateY="12.469"> + android:translateY="11.969"> <group android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0" android:scaleX="1" @@ -260,18 +165,7 @@ android:fillAlpha="1" android:fillColor="#ffffff" android:fillType="nonZero" - android:pathData=" M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c " /> - </group> - <group - android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0" - android:scaleX="1" - android:scaleY="1"> - <path - android:name="_R_G_L_0_G_D_0_P_1" - android:fillAlpha="1" - android:fillColor="#ffffff" - android:fillType="nonZero" - android:pathData=" M1.51 1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.49 1.51,1.49c " /> + android:pathData=" M-3.5 8.5 C-3.5,8.5 -3.5,10 -3.5,10 C-3.5,10 0,9 0,9 C0,9 3.5,10 3.5,10 C3.5,10 3.5,8.5 3.5,8.5 C3.5,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5 C1.5,1.5 10,4 10,4 C10,4 10,2 10,2 C10,2 1.5,-3 1.5,-3 C1.5,-3 1.5,-8.02 1.5,-8.02 C1.5,-8.44 1.2,-9.52 0,-9.52 C-1.19,-9.52 -1.5,-8.44 -1.5,-8.02 C-1.5,-8.02 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -3.5,8.5 -3.5,8.5c " /> </group> </group> </group> diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml index acb47f7a037c..2d67d95ab17e 100644 --- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml +++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml @@ -14,8 +14,9 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.dreams.complication.DoubleShadowTextClock +<com.android.systemui.shared.shadow.DoubleShadowTextClock xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/time_view" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -25,4 +26,13 @@ android:format24Hour="@string/dream_time_complication_24_hr_time_format" android:fontFeatureSettings="pnum, lnum" android:letterSpacing="0.02" - android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"/> + android:textSize="@dimen/dream_overlay_complication_clock_time_text_size" + app:keyShadowBlur="@dimen/dream_overlay_clock_key_text_shadow_radius" + app:keyShadowOffsetX="@dimen/dream_overlay_clock_key_text_shadow_dx" + app:keyShadowOffsetY="@dimen/dream_overlay_clock_key_text_shadow_dy" + app:keyShadowAlpha="0.3" + app:ambientShadowBlur="@dimen/dream_overlay_clock_ambient_text_shadow_radius" + app:ambientShadowOffsetX="@dimen/dream_overlay_clock_ambient_text_shadow_dx" + app:ambientShadowOffsetY="@dimen/dream_overlay_clock_ambient_text_shadow_dy" + app:ambientShadowAlpha="0.3" +/> diff --git a/packages/SystemUI/res/layout/media_projection_app_selector.xml b/packages/SystemUI/res/layout/media_projection_app_selector.xml index 4ad6849e9c45..226bc6afc5ab 100644 --- a/packages/SystemUI/res/layout/media_projection_app_selector.xml +++ b/packages/SystemUI/res/layout/media_projection_app_selector.xml @@ -36,13 +36,14 @@ android:background="@*android:drawable/bottomsheet_background"> <ImageView - android:id="@*android:id/icon" android:layout_width="@dimen/media_projection_app_selector_icon_size" android:layout_height="@dimen/media_projection_app_selector_icon_size" android:layout_marginTop="@*android:dimen/chooser_edge_margin_normal" android:layout_marginBottom="@*android:dimen/chooser_edge_margin_normal" android:importantForAccessibility="no" - android:tint="?android:attr/textColorPrimary"/> + android:tint="?android:attr/textColorPrimary" + android:src="@drawable/ic_present_to_all" + /> <TextView android:id="@*android:id/title" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res/layout/media_projection_recent_tasks.xml b/packages/SystemUI/res/layout/media_projection_recent_tasks.xml new file mode 100644 index 000000000000..a2b3c404f077 --- /dev/null +++ b/packages/SystemUI/res/layout/media_projection_recent_tasks.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2022 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:orientation="vertical" + android:background="?android:attr/colorBackground" + > + + <FrameLayout + android:layout_width="match_parent" + android:layout_height="256dp"> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/media_projection_recent_tasks_recycler" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="gone" + /> + + <ProgressBar + android:id="@+id/media_projection_recent_tasks_loader" + android:layout_width="@dimen/media_projection_app_selector_loader_size" + android:layout_height="@dimen/media_projection_app_selector_loader_size" + android:layout_gravity="center" + android:indeterminate="true" + android:indeterminateOnly="true" /> + </FrameLayout> + + <!-- Divider line --> + <ImageView + android:layout_width="72dp" + android:layout_height="2dp" + android:layout_marginBottom="8dp" + android:layout_marginTop="24dp" + android:importantForAccessibility="no" + android:src="@*android:drawable/ic_drag_handle" + android:tint="?android:attr/textColorSecondary" /> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/media_projection_task_item.xml b/packages/SystemUI/res/layout/media_projection_task_item.xml new file mode 100644 index 000000000000..75f73cd7a1e9 --- /dev/null +++ b/packages/SystemUI/res/layout/media_projection_task_item.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2022 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. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:gravity="center" + android:orientation="vertical"> + + <ImageView + android:id="@+id/task_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_margin="8dp" + android:importantForAccessibility="no" /> + + <!-- TODO(b/240924926) use a custom view that will handle thumbnail cropping correctly --> + <!-- TODO(b/240924926) dynamically change the view size based on the screen size --> + <ImageView + android:id="@+id/task_thumbnail" + android:layout_width="100dp" + android:layout_height="216dp" + android:scaleType="centerCrop" + /> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index 0c57b934d27e..8388b67c1fa1 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -103,6 +103,7 @@ android:layout_width="match_parent" android:layout_weight="1" android:background="@android:color/transparent" + android:visibility="invisible" android:clipChildren="false" android:clipToPadding="false" /> </LinearLayout> diff --git a/packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json b/packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json index b1616094ade4..09ed22560885 100644 --- a/packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json +++ b/packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json @@ -1 +1 @@ -{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_portrait_base_topleft","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":6,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null_Circle","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey905","cl":"grey905","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"circle mask 3","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Finger_Flipped","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[-24.98,-35.709,0],"ix":2,"l":2},"a":{"a":0,"k":[31.791,75.23,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.03,5.25],[-2.83,8.98],[-5.59,-0.26],[2.52,-11.02]],"o":[[-2.85,12.77],[2.07,-14.96],[1.9,-6],[1.4,8.05],[0,0]],"v":[[7.5,4.99],[-10.09,19.69],[-3.59,-16.61],[8.69,-24.92],[7.5,5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.8,24.94],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-7.01,22.23],[-1.2,-27.39],[4.09,-26.79],[15.73,14.18]],"o":[[5.64,-17.93],[2.45,56.06],[-22.4,-1.77],[17.73,-51.82]],"v":[[-7.57,-66.9],[30.82,-44.76],[26.65,75.23],[-31.78,50.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[31.79,75.23],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"circle mask 7","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".grey600","cl":"grey600","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"circle mask","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".grey904","cl":"grey904","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"circle mask 6","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey903","cl":"grey903","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.238,5.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"circle mask 2","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".blue400","cl":"blue400","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,17.13],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,20.979],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"circle mask 4","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":1,"nm":".grey902","cl":"grey902","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"circle mask 5","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":1,"nm":".black","cl":"black","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".grey800","cl":"grey800","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":".grey901","cl":"grey901","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-87.156,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[100.25,-94.656,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"Shape Layer 4","parent":6,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":".grey900","cl":"grey900","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file +{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Portrait_Base_TopLeft","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":6,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null_Circle","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey600","cl":"grey600","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"circle mask 3","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Finger_Flipped","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[-24.98,-35.709,0],"ix":2,"l":2},"a":{"a":0,"k":[31.791,75.23,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.03,5.25],[-2.83,8.98],[-5.59,-0.26],[2.52,-11.02]],"o":[[-2.85,12.77],[2.07,-14.96],[1.9,-6],[1.4,8.05],[0,0]],"v":[[7.5,4.99],[-10.09,19.69],[-3.59,-16.61],[8.69,-24.92],[7.5,5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.8,24.94],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-7.01,22.23],[-1.2,-27.39],[4.09,-26.79],[15.73,14.18]],"o":[[5.64,-17.93],[2.45,56.06],[-22.4,-1.77],[17.73,-51.82]],"v":[[-7.57,-66.9],[30.82,-44.76],[26.65,75.23],[-31.78,50.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[31.79,75.23],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"circle mask 7","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".grey600","cl":"grey600","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"circle mask","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".grey900","cl":"grey900","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"circle mask 6","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey900","cl":"grey900","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.238,5.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"circle mask 2","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".blue400","cl":"blue400","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,17.13],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,20.979],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"circle mask 4","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":1,"nm":".grey900","cl":"grey900","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"circle mask 5","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":1,"nm":".black","cl":"black","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".grey800","cl":"grey800","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":".grey900","cl":"grey900","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-87.156,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[100.25,-94.656,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"Shape Layer 4","parent":6,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":".grey900","cl":"grey900","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 05c5df21a360..839fa894c034 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -251,7 +251,7 @@ <string name="quick_settings_connecting" msgid="2381969772953268809">"S\'està connectant..."</string> <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Punt d\'accés Wi-Fi"</string> <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"S\'està activant…"</string> - <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Economitzador activat"</string> + <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Estalvi dades activat"</string> <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# dispositiu}other{# dispositius}}"</string> <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Llanterna"</string> <string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Càmera en ús"</string> @@ -593,8 +593,8 @@ <string name="accessibility_long_click_tile" msgid="210472753156768705">"Obre la configuració"</string> <string name="accessibility_status_bar_headphones" msgid="1304082414912647414">"Auriculars connectats"</string> <string name="accessibility_status_bar_headset" msgid="2699275863720926104">"Auriculars connectats"</string> - <string name="data_saver" msgid="3484013368530820763">"Economitzador de dades"</string> - <string name="accessibility_data_saver_on" msgid="5394743820189757731">"L\'Economitzador de dades està activat"</string> + <string name="data_saver" msgid="3484013368530820763">"Estalvi de dades"</string> + <string name="accessibility_data_saver_on" msgid="5394743820189757731">"L\'Estalvi de dades està activat"</string> <string name="switch_bar_on" msgid="1770868129120096114">"Activat"</string> <string name="switch_bar_off" msgid="5669805115416379556">"Desactivat"</string> <string name="tile_unavailable" msgid="3095879009136616920">"No disponible"</string> @@ -623,7 +623,7 @@ <string name="right_keycode" msgid="2480715509844798438">"Codi de tecla de la dreta"</string> <string name="left_icon" msgid="5036278531966897006">"Icona de l\'esquerra"</string> <string name="right_icon" msgid="1103955040645237425">"Icona de la dreta"</string> - <string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantén premut i arrossega per afegir mosaics"</string> + <string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantén premut i arrossega per afegir icones"</string> <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Mantén premut i arrossega per reorganitzar els mosaics"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrossega aquí per suprimir"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Necessites com a mínim <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> mosaics"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 8e4aa79cd397..4cebc9bb2a9a 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -196,9 +196,9 @@ <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Азырақ уақыт."</string> <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Экранды трансляциялау тоқтатылды."</string> <string name="accessibility_brightness" msgid="5391187016177823721">"Дисплей жарықтығы"</string> - <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Мобильдік деректер кідіртілді"</string> + <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Мобильдік интернет кідіртілді"</string> <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Деректер кідіртілді"</string> - <string name="data_usage_disabled_dialog" msgid="7933201635215099780">"Белгіленген деректер шегіне жеттіңіз. Мобильдік деректер енді пайдаланылмайды.\n\nЕгер жалғастырсаңыз, деректер трафигі үшін ақы алынуы мүмкін."</string> + <string name="data_usage_disabled_dialog" msgid="7933201635215099780">"Белгіленген деректер шегіне жеттіңіз. Мобильдік интернет енді пайдаланылмайды.\n\nЕгер жалғастырсаңыз, деректер трафигі үшін ақы алынуы мүмкін."</string> <string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"Жалғастыру"</string> <string name="accessibility_location_active" msgid="2845747916764660369">"Орын өтініштері қосылған"</string> <string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Датчиктер өшірулі."</string> @@ -255,7 +255,7 @@ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# құрылғы}other{# құрылғы}}"</string> <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Қалта шам"</string> <string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Камера қолданылып жатыр"</string> - <string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Мобильдік деректер"</string> + <string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Мобильдік интернет"</string> <string name="quick_settings_cellular_detail_data_usage" msgid="6105969068871138427">"Дерек шығыны"</string> <string name="quick_settings_cellular_detail_remaining_data" msgid="1136599216568805644">"Қалған деректер"</string> <string name="quick_settings_cellular_detail_over_limit" msgid="4561921367680636235">"Шектен асу"</string> @@ -598,8 +598,7 @@ <string name="switch_bar_on" msgid="1770868129120096114">"Қосулы"</string> <string name="switch_bar_off" msgid="5669805115416379556">"Өшірулі"</string> <string name="tile_unavailable" msgid="3095879009136616920">"Қолжетімді емес"</string> - <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) --> - <skip /> + <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"толығырақ"</string> <string name="nav_bar" msgid="4642708685386136807">"Шарлау тақтасы"</string> <string name="nav_bar_layout" msgid="4716392484772899544">"Формат"</string> <string name="left_nav_bar_button_type" msgid="2634852842345192790">"Қосымша сол жақ түйме түрі"</string> @@ -713,7 +712,7 @@ <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"Мазаламау режимі автоматты ереже немесе қолданба арқылы қосылды."</string> <string name="running_foreground_services_title" msgid="5137313173431186685">"Фонда жұмыс істеп тұрған қолданбалар"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"Батарея мен деректер трафигі туралы білу үшін түртіңіз"</string> - <string name="mobile_data_disable_title" msgid="5366476131671617790">"Мобильдік деректер өшірілсін бе?"</string> + <string name="mobile_data_disable_title" msgid="5366476131671617790">"Мобильдік интернет өшірілсін бе?"</string> <string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> операторы арқылы деректерге немесе интернетке кіре алмайсыз. Интернетке тек Wi-Fi арқылы кіресіз."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"операторыңыз"</string> <string name="touch_filtered_warning" msgid="8119511393338714836">"Басқа қолданба рұқсат сұрауын жасырып тұрғандықтан, параметрлер жауабыңызды растай алмайды."</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 665efa51a6e8..6a2276a8efcb 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -327,9 +327,9 @@ <string name="do_disclosure_generic" msgid="4896482821974707167">"हे डिव्हाइस तुमच्या संस्थेचे आहे"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> चे आहे"</string> <string name="do_financed_disclosure_with_name" msgid="6723004643314467864">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> द्वारे पुरवले गेले आहे"</string> - <string name="phone_hint" msgid="6682125338461375925">"फोनसाठी चिन्हावरून स्वाइप करा"</string> - <string name="voice_hint" msgid="7476017460191291417">"व्हॉइस सहाय्यासाठी चिन्हावरून स्वाइप करा"</string> - <string name="camera_hint" msgid="4519495795000658637">"कॅमेर्यासाठी चिन्हावरून स्वाइप करा"</string> + <string name="phone_hint" msgid="6682125338461375925">"फोनसाठी आयकनवरून स्वाइप करा"</string> + <string name="voice_hint" msgid="7476017460191291417">"व्हॉइस सहाय्यासाठी आयकनवरून स्वाइप करा"</string> + <string name="camera_hint" msgid="4519495795000658637">"कॅमेर्यासाठी आयकनवरून स्वाइप करा"</string> <string name="interruption_level_none_with_warning" msgid="8394434073508145437">"संपूर्ण शांतता. हे स्क्रीन रीडर ना देखील शांत करेल."</string> <string name="interruption_level_none" msgid="219484038314193379">"संपूर्ण शांतता"</string> <string name="interruption_level_priority" msgid="661294280016622209">"केवळ प्राधान्य"</string> diff --git a/packages/SystemUI/res/values-ro-ldrtl/strings.xml b/packages/SystemUI/res/values-ro-ldrtl/strings.xml index e167b41c680c..a7cd33cd21da 100644 --- a/packages/SystemUI/res/values-ro-ldrtl/strings.xml +++ b/packages/SystemUI/res/values-ro-ldrtl/strings.xml @@ -19,5 +19,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Trageți spre stânga pentru a comuta rapid între aplicații"</string> + <string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Trage spre stânga pentru a comuta rapid între aplicații"</string> </resources> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 661be0074e69..7612dfca83b3 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -20,28 +20,28 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4811759950673118541">"UI sistem"</string> - <string name="battery_low_title" msgid="5319680173344341779">"Activați Economisirea bateriei?"</string> - <string name="battery_low_description" msgid="3282977755476423966">"Mai aveți <xliff:g id="PERCENTAGE">%s</xliff:g> din baterie. Economisirea bateriei activează Tema întunecată, restricționează activitatea în fundal și amână notificările."</string> + <string name="battery_low_title" msgid="5319680173344341779">"Activezi Economisirea bateriei?"</string> + <string name="battery_low_description" msgid="3282977755476423966">"Mai ai <xliff:g id="PERCENTAGE">%s</xliff:g> din baterie. Economisirea bateriei activează Tema întunecată, restricționează activitatea în fundal și amână notificările."</string> <string name="battery_low_intro" msgid="5148725009653088790">"Economisirea bateriei activează Tema întunecată, restricționează activitatea în fundal și amână notificările."</string> <string name="battery_low_percent_format" msgid="4276661262843170964">"Procent rămas din baterie: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="invalid_charger_title" msgid="938685362320735167">"Nu se poate realiza încărcarea prin USB"</string> - <string name="invalid_charger_text" msgid="2339310107232691577">"Folosiți încărcătorul livrat împreună cu dispozitivul"</string> - <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Activați economisirea bateriei?"</string> + <string name="invalid_charger_text" msgid="2339310107232691577">"Folosește încărcătorul livrat împreună cu dispozitivul"</string> + <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Activezi economisirea bateriei?"</string> <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Despre Economisirea bateriei"</string> - <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Activați"</string> - <string name="battery_saver_start_action" msgid="8353766979886287140">"Activați"</string> + <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Activează"</string> + <string name="battery_saver_start_action" msgid="8353766979886287140">"Activează"</string> <string name="battery_saver_dismiss_action" msgid="7199342621040014738">"Nu, mulțumesc"</string> <string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Rotire automată a ecranului"</string> - <string name="usb_device_permission_prompt" msgid="4414719028369181772">"Permiți <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string> + <string name="usb_device_permission_prompt" msgid="4414719028369181772">"Permiți ca <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string> <string name="usb_device_permission_prompt_warn" msgid="2309129784984063656">"Permiți accesul aplicației <xliff:g id="APPLICATION">%1$s</xliff:g> la <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nPermisiunea de înregistrare nu a fost acordată aplicației, dar aceasta poate să înregistreze conținut audio prin intermediul acestui dispozitiv USB."</string> <string name="usb_audio_device_permission_prompt_title" msgid="4221351137250093451">"Permiți ca <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string> - <string name="usb_audio_device_confirm_prompt_title" msgid="8828406516732985696">"Deschideți <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string> - <string name="usb_audio_device_prompt_warn" msgid="2504972133361130335">"Permisiunea de înregistrare nu a fost acordată aplicației, dar aceasta poate să înregistreze conținut audio prin intermediul acestui dispozitiv USB. Dacă folosiți <xliff:g id="APPLICATION">%1$s</xliff:g> cu acest dispozitiv, acest lucru vă poate împiedica să auziți apeluri, notificări și alarme."</string> - <string name="usb_audio_device_prompt" msgid="7944987408206252949">"Dacă folosiți <xliff:g id="APPLICATION">%1$s</xliff:g> cu acest dispozitiv, acest lucru vă poate împiedica să auziți apeluri, notificări și alarme."</string> - <string name="usb_accessory_permission_prompt" msgid="717963550388312123">"Permiți <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string> - <string name="usb_device_confirm_prompt" msgid="4091711472439910809">"Deschideți <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string> - <string name="usb_device_confirm_prompt_warn" msgid="990208659736311769">"Deschide <xliff:g id="APPLICATION">%1$s</xliff:g> pentru a gestiona <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nPermisiunea de înregistrare nu a fost acordată aplicației, dar aceasta poate să înregistreze conținut audio prin intermediul acestui dispozitiv USB."</string> - <string name="usb_accessory_confirm_prompt" msgid="5728408382798643421">"Deschideți <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string> + <string name="usb_audio_device_confirm_prompt_title" msgid="8828406516732985696">"Deschizi <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string> + <string name="usb_audio_device_prompt_warn" msgid="2504972133361130335">"Permisiunea de înregistrare nu a fost acordată aplicației, dar aceasta poate să înregistreze conținut audio prin intermediul acestui dispozitiv USB. Dacă folosești <xliff:g id="APPLICATION">%1$s</xliff:g> cu acest dispozitiv, acest lucru te poate împiedica să auzi apeluri, notificări și alarme."</string> + <string name="usb_audio_device_prompt" msgid="7944987408206252949">"Dacă folosești <xliff:g id="APPLICATION">%1$s</xliff:g> cu acest dispozitiv, acest lucru te poate împiedica să auzi apeluri, notificări și alarme."</string> + <string name="usb_accessory_permission_prompt" msgid="717963550388312123">"Permiți ca <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string> + <string name="usb_device_confirm_prompt" msgid="4091711472439910809">"Deschizi <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string> + <string name="usb_device_confirm_prompt_warn" msgid="990208659736311769">"Deschizi <xliff:g id="APPLICATION">%1$s</xliff:g> pentru a gestiona <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nPermisiunea de înregistrare nu a fost acordată aplicației, dar aceasta poate să înregistreze conținut audio prin intermediul acestui dispozitiv USB."</string> + <string name="usb_accessory_confirm_prompt" msgid="5728408382798643421">"Deschizi <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string> <string name="usb_accessory_uri_prompt" msgid="6756649383432542382">"Aplic. instal. nu funcț. cu acest acces. USB. Află despre acest accesoriu la <xliff:g id="URL">%1$s</xliff:g>"</string> <string name="title_usb_accessory" msgid="1236358027511638648">"Accesoriu USB"</string> <string name="label_view" msgid="6815442985276363364">"Afișează"</string> @@ -52,21 +52,21 @@ <string name="usb_debugging_always" msgid="4003121804294739548">"Permite întotdeauna de pe acest computer"</string> <string name="usb_debugging_allow" msgid="1722643858015321328">"Permite"</string> <string name="usb_debugging_secondary_user_title" msgid="7843050591380107998">"Remedierea erorilor prin USB nu este permisă"</string> - <string name="usb_debugging_secondary_user_message" msgid="3740347841470403244">"Utilizatorul conectat momentan pe acest dispozitiv nu poate activa remedierea erorilor prin USB. Pentru a folosi această funcție, comutați la utilizatorul principal."</string> - <string name="hdmi_cec_set_menu_language_title" msgid="1259765420091503742">"Schimbați limba de sistem la <xliff:g id="LANGUAGE">%1$s</xliff:g>?"</string> + <string name="usb_debugging_secondary_user_message" msgid="3740347841470403244">"Utilizatorul conectat momentan pe acest dispozitiv nu poate activa remedierea erorilor prin USB. Pentru a folosi această funcție, comută la utilizatorul principal."</string> + <string name="hdmi_cec_set_menu_language_title" msgid="1259765420091503742">"Schimbi limba de sistem la <xliff:g id="LANGUAGE">%1$s</xliff:g>?"</string> <string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Alt dispozitiv solicită schimbarea limbii de sistem"</string> - <string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Schimbați limba"</string> - <string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Păstrați limba actuală"</string> + <string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Schimbă limba"</string> + <string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Păstrează limba actuală"</string> <string name="wifi_debugging_title" msgid="7300007687492186076">"Permiți remedierea erorilor wireless în această rețea?"</string> <string name="wifi_debugging_message" msgid="5461204211731802995">"Numele rețelei (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAdresa Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string> <string name="wifi_debugging_always" msgid="2968383799517975155">"Permite întotdeauna în această rețea"</string> <string name="wifi_debugging_allow" msgid="4573224609684957886">"Permite"</string> <string name="wifi_debugging_secondary_user_title" msgid="2493201475880517725">"Remedierea erorilor wireless nu este permisă"</string> - <string name="wifi_debugging_secondary_user_message" msgid="4492383073970079751">"Utilizatorul conectat momentan pe acest dispozitiv nu poate activa remedierea erorilor wireless. Pentru a folosi această funcție, comutați la utilizatorul principal."</string> + <string name="wifi_debugging_secondary_user_message" msgid="4492383073970079751">"Utilizatorul conectat momentan pe acest dispozitiv nu poate activa remedierea erorilor wireless. Pentru a folosi această funcție, comută la utilizatorul principal."</string> <string name="usb_contaminant_title" msgid="894052515034594113">"Portul USB a fost dezactivat"</string> - <string name="usb_contaminant_message" msgid="7730476585174719805">"Pentru a vă proteja dispozitivul de lichide sau reziduuri, portul USB este dezactivat și nu va detecta niciun accesoriu.\n\nVeți primi o notificare când puteți folosi din nou portul USB."</string> + <string name="usb_contaminant_message" msgid="7730476585174719805">"Pentru a proteja dispozitivul de lichide sau reziduuri, portul USB este dezactivat și nu va detecta niciun accesoriu.\n\nVei primi o notificare când poți folosi din nou portul USB."</string> <string name="usb_port_enabled" msgid="531823867664717018">"Portul USB a fost activat pentru a detecta încărcătoarele și accesoriile"</string> - <string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"Activați USB"</string> + <string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"Activează USB"</string> <string name="learn_more" msgid="4690632085667273811">"Mai multe"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"Captură de ecran"</string> <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock dezactivat"</string> @@ -74,15 +74,15 @@ <string name="screenshot_saving_title" msgid="2298349784913287333">"Se salvează captura de ecran..."</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Captură de ecran salvată"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Nu s-a putut salva captura de ecran"</string> - <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"Pentru a salva captura de ecran, trebuie să deblocați dispozitivul"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Încercați să faceți din nou o captură de ecran"</string> + <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"Pentru a salva captura de ecran, trebuie să deblochezi dispozitivul"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Încearcă să faci din nou o captură de ecran"</string> <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"Nu se poate salva captura de ecran"</string> - <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Crearea capturilor de ecran nu este permisă de aplicație sau de organizația dvs."</string> + <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Crearea capturilor de ecran nu e permisă de aplicație sau de organizația ta"</string> <string name="screenshot_blocked_by_admin" msgid="5486757604822795797">"Administratorul IT a blocat crearea capturilor de ecran"</string> - <string name="screenshot_edit_label" msgid="8754981973544133050">"Editați"</string> - <string name="screenshot_edit_description" msgid="3333092254706788906">"Editați captura de ecran"</string> + <string name="screenshot_edit_label" msgid="8754981973544133050">"Editează"</string> + <string name="screenshot_edit_description" msgid="3333092254706788906">"Editează captura de ecran"</string> <string name="screenshot_share_description" msgid="2861628935812656612">"Trimite captura de ecran"</string> - <string name="screenshot_scroll_label" msgid="2930198809899329367">"Surprindeți mai mult"</string> + <string name="screenshot_scroll_label" msgid="2930198809899329367">"Surprinde mai mult"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Închide captura de ecran"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Previzualizare a capturii de ecran"</string> <string name="screenshot_top_boundary_pct" msgid="2520148599096479332">"Marginea de sus la <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string> @@ -92,14 +92,14 @@ <string name="screenrecord_name" msgid="2596401223859996572">"Recorder pentru ecran"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Se procesează înregistrarea"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificare în curs pentru o sesiune de înregistrare a ecranului"</string> - <string name="screenrecord_start_label" msgid="1750350278888217473">"Începeți înregistrarea?"</string> + <string name="screenrecord_start_label" msgid="1750350278888217473">"Începi înregistrarea?"</string> <string name="screenrecord_description" msgid="1123231719680353736">"În timpul înregistrării, sistemul Android poate captura informațiile sensibile vizibile pe ecran sau redate pe dispozitiv. Aici sunt incluse parole, informații de plată, fotografii, mesaje și conținut audio."</string> - <string name="screenrecord_audio_label" msgid="6183558856175159629">"Înregistrați conținut audio"</string> + <string name="screenrecord_audio_label" msgid="6183558856175159629">"Înregistrează audio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Conținutul audio de la dispozitiv"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sunetul de la dispozitiv, precum muzică, apeluri și tonuri de sonerie"</string> <string name="screenrecord_mic_label" msgid="2111264835791332350">"Microfon"</string> <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Conținutul audio de la dispozitiv și microfon"</string> - <string name="screenrecord_start" msgid="330991441575775004">"Începeți"</string> + <string name="screenrecord_start" msgid="330991441575775004">"Începe"</string> <string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Se înregistrează ecranul"</string> <string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Se înregistrează ecranul și conținutul audio"</string> <string name="screenrecord_taps_label" msgid="1595690528298857649">"Afișează atingerile de pe ecran"</string> @@ -113,52 +113,52 @@ <string name="accessibility_home" msgid="5430449841237966217">"Ecranul de pornire"</string> <string name="accessibility_menu" msgid="2701163794470513040">"Meniu"</string> <string name="accessibility_accessibility_button" msgid="4089042473497107709">"Accesibilitate"</string> - <string name="accessibility_rotate_button" msgid="1238584767612362586">"Rotiți ecranul"</string> + <string name="accessibility_rotate_button" msgid="1238584767612362586">"Rotește ecranul"</string> <string name="accessibility_recent" msgid="901641734769533575">"Recente"</string> <string name="accessibility_camera_button" msgid="2938898391716647247">"Cameră foto"</string> <string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string> <string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asistent vocal"</string> <string name="accessibility_wallet_button" msgid="1458258783460555507">"Portofel"</string> <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Scanner de coduri QR"</string> - <string name="accessibility_unlock_button" msgid="122785427241471085">"Deblocați"</string> + <string name="accessibility_unlock_button" msgid="122785427241471085">"Deblochează"</string> <string name="accessibility_lock_icon" msgid="661492842417875775">"Dispozitiv blocat"</string> <string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanarea chipului"</string> <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Trimite"</string> <string name="cancel" msgid="1089011503403416730">"Anulează"</string> - <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmați"</string> + <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmă"</string> <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Încearcă din nou"</string> <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Atinge pentru a anula autentificarea"</string> <string name="biometric_dialog_face_icon_description_idle" msgid="4351777022315116816">"Încearcă din nou"</string> <string name="biometric_dialog_face_icon_description_authenticating" msgid="3401633342366146535">"Se caută chipul"</string> <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Chip autentificat"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmat"</string> - <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Atingeți Confirmați pentru a finaliza"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="1092050545851021991">"S-a deblocat cu ajutorul feței. Apasă pictograma de deblocare pentru a continua"</string> + <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Atinge Confirm pentru a finaliza"</string> + <string name="biometric_dialog_tap_confirm_with_face" msgid="1092050545851021991">"Deblocat facial. Apasă pictograma Deblocare ca să continui."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"S-a deblocat cu ajutorul feței. Apasă pentru a continua."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Chipul a fost recunoscut. Apasă pentru a continua."</string> - <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Chip recunoscut. Apăsați pictograma de deblocare să continuați."</string> + <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Chip recunoscut. Apasă pictograma Deblocare ca să continui."</string> <string name="biometric_dialog_authenticated" msgid="7337147327545272484">"Autentificat"</string> - <string name="biometric_dialog_use_pin" msgid="8385294115283000709">"Folosiți PIN-ul"</string> - <string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"Folosiți modelul"</string> - <string name="biometric_dialog_use_password" msgid="3445033859393474779">"Folosiți parola"</string> + <string name="biometric_dialog_use_pin" msgid="8385294115283000709">"Folosește PIN-ul"</string> + <string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"Folosește modelul"</string> + <string name="biometric_dialog_use_password" msgid="3445033859393474779">"Folosește parola"</string> <string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"PIN greșit"</string> <string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Model greșit"</string> <string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Parolă greșită"</string> <string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Prea multe încercări incorecte.\nÎncearcă din nou peste <xliff:g id="NUMBER">%d</xliff:g> secunde."</string> <string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Încearcă din nou. Încercarea <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> din <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string> - <string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Datele dvs. vor fi șterse"</string> - <string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Dacă la următoarea încercare introduceți un model incorect, datele de pe acest dispozitiv vor fi șterse."</string> - <string name="biometric_dialog_last_pin_attempt_before_wipe_device" msgid="9151756675698215723">"Dacă la următoarea încercare introduceți un cod PIN incorect, datele de pe acest dispozitiv vor fi șterse."</string> - <string name="biometric_dialog_last_password_attempt_before_wipe_device" msgid="2363778585575998317">"Dacă la următoarea încercare introduceți o parolă incorectă, datele de pe acest dispozitiv vor fi șterse."</string> - <string name="biometric_dialog_last_pattern_attempt_before_wipe_user" msgid="8400180746043407270">"Dacă la următoarea încercare introduceți un model incorect, acest utilizator va fi șters."</string> - <string name="biometric_dialog_last_pin_attempt_before_wipe_user" msgid="4159878829962411168">"Dacă la următoarea încercare introduceți un cod PIN incorect, acest utilizator va fi șters."</string> - <string name="biometric_dialog_last_password_attempt_before_wipe_user" msgid="4695682515465063885">"Dacă la următoarea încercare introduceți o parolă incorectă, acest utilizator va fi șters."</string> - <string name="biometric_dialog_last_pattern_attempt_before_wipe_profile" msgid="6045224069529284686">"Dacă la următoarea încercare introduceți un model incorect, profilul de serviciu și datele sale vor fi șterse."</string> - <string name="biometric_dialog_last_pin_attempt_before_wipe_profile" msgid="545567685899091757">"Dacă la următoarea încercare introduceți un cod PIN incorect, profilul de serviciu și datele sale vor fi șterse."</string> - <string name="biometric_dialog_last_password_attempt_before_wipe_profile" msgid="8538032972389729253">"Dacă la următoarea încercare introduceți o parolă incorectă, profilul de serviciu și datele sale vor fi șterse."</string> + <string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Datele tale vor fi șterse"</string> + <string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Dacă la următoarea încercare introduci un model incorect, datele de pe acest dispozitiv vor fi șterse."</string> + <string name="biometric_dialog_last_pin_attempt_before_wipe_device" msgid="9151756675698215723">"Dacă la următoarea încercare introduci un cod PIN incorect, datele de pe acest dispozitiv vor fi șterse."</string> + <string name="biometric_dialog_last_password_attempt_before_wipe_device" msgid="2363778585575998317">"Dacă la următoarea încercare introduci o parolă incorectă, datele de pe acest dispozitiv vor fi șterse."</string> + <string name="biometric_dialog_last_pattern_attempt_before_wipe_user" msgid="8400180746043407270">"Dacă la următoarea încercare introduci un model incorect, acest utilizator va fi șters."</string> + <string name="biometric_dialog_last_pin_attempt_before_wipe_user" msgid="4159878829962411168">"Dacă la următoarea încercare introduci un cod PIN incorect, acest utilizator va fi șters."</string> + <string name="biometric_dialog_last_password_attempt_before_wipe_user" msgid="4695682515465063885">"Dacă la următoarea încercare introduci o parolă incorectă, acest utilizator va fi șters."</string> + <string name="biometric_dialog_last_pattern_attempt_before_wipe_profile" msgid="6045224069529284686">"Dacă la următoarea încercare introduci un model incorect, profilul de serviciu și datele sale vor fi șterse."</string> + <string name="biometric_dialog_last_pin_attempt_before_wipe_profile" msgid="545567685899091757">"Dacă la următoarea încercare introduci un cod PIN incorect, profilul de serviciu și datele sale vor fi șterse."</string> + <string name="biometric_dialog_last_password_attempt_before_wipe_profile" msgid="8538032972389729253">"Dacă la următoarea încercare introduci o parolă incorectă, profilul de serviciu și datele sale vor fi șterse."</string> <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Atinge senzorul de amprente"</string> <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="4465698996175640549">"Pictograma amprentă"</string> - <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Chipul nu a fost recunoscut. Folosiți amprenta."</string> + <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Chipul nu a fost recunoscut. Folosește amprenta."</string> <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) --> <skip /> <string name="keyguard_face_failed" msgid="9044619102286917151">"Chip nerecunoscut"</string> @@ -198,8 +198,8 @@ <string name="accessibility_brightness" msgid="5391187016177823721">"Luminozitatea ecranului"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Datele mobile sunt întrerupte"</string> <string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Conexiunea de date este întreruptă"</string> - <string name="data_usage_disabled_dialog" msgid="7933201635215099780">"A fost atinsă limita de date setată. Datele mobile nu mai sunt folosite.\n\nDacă reluați, este posibil să se aplice taxe pentru utilizarea datelor."</string> - <string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"Reluați"</string> + <string name="data_usage_disabled_dialog" msgid="7933201635215099780">"A fost atinsă limita de date setată. Datele mobile nu mai sunt folosite.\n\nDacă reiei, se pot aplica taxe pentru utilizarea datelor."</string> + <string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"Reia"</string> <string name="accessibility_location_active" msgid="2845747916764660369">"Solicitări locație active"</string> <string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Dezactivarea senzorilor este activă"</string> <string name="accessibility_clear_all" msgid="970525598287244592">"Șterge toate notificările."</string> @@ -280,56 +280,56 @@ <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Serviciul NFC este dezactivat"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Serviciul NFC este activat"</string> <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Înregistrarea ecranului"</string> - <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Începeți"</string> + <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Începe"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Oprește"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modul cu o mână"</string> - <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblocați microfonul dispozitivului?"</string> - <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblocați camera dispozitivului?"</string> - <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblocați camera și microfonul dispozitivului?"</string> - <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Astfel, deblocați accesul pentru toate aplicațiile și serviciile care au permisiunea de a folosi microfonul."</string> - <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Astfel, deblocați accesul pentru toate aplicațiile și serviciile care au permisiunea de a folosi camera."</string> - <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Astfel, deblocați accesul pentru toate aplicațiile și serviciile care au permisiunea de a folosi camera sau microfonul."</string> + <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblochezi microfonul dispozitivului?"</string> + <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblochezi camera dispozitivului?"</string> + <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblochezi camera și microfonul dispozitivului?"</string> + <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Astfel, deblochezi accesul pentru toate aplicațiile și serviciile care au permisiunea de a folosi microfonul."</string> + <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Astfel, deblochezi accesul pentru toate aplicațiile și serviciile care au permisiunea de a folosi camera."</string> + <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Astfel, deblochezi accesul pentru toate aplicațiile și serviciile care au permisiunea de a folosi camera sau microfonul."</string> <string name="sensor_privacy_start_use_mic_blocked_dialog_title" msgid="2640140287496469689">"Microfonul este blocat"</string> <string name="sensor_privacy_start_use_camera_blocked_dialog_title" msgid="7398084286822440384">"Camera este blocată"</string> <string name="sensor_privacy_start_use_mic_camera_blocked_dialog_title" msgid="195236134743281973">"Microfonul și camera sunt blocate"</string> - <string name="sensor_privacy_start_use_mic_blocked_dialog_content" msgid="2138318880682877747">"Pentru deblocare, deplasați comutatorul de confidențialitate de pe dispozitiv în poziția Microfon activat pentru a permite accesul la microfon. Consultați manualul dispozitivului ca să găsiți comutatorul de confidențialitate."</string> - <string name="sensor_privacy_start_use_camera_blocked_dialog_content" msgid="7216015168047965948">"Pentru deblocare, deplasați comutatorul de confidențialitate de pe dispozitiv în poziția Cameră foto activată pentru a permite accesul la cameră. Consultați manualul dispozitivului ca să găsiți comutatorul de confidențialitate."</string> - <string name="sensor_privacy_start_use_mic_camera_blocked_dialog_content" msgid="3960837827570483762">"Pentru deblocare, deplasați comutatorul de confidențialitate de pe dispozitiv în poziția Deblocat(ă) pentru a permite accesul. Consultați manualul dispozitivului ca să găsiți comutatorul de confidențialitate."</string> + <string name="sensor_privacy_start_use_mic_blocked_dialog_content" msgid="2138318880682877747">"Pentru deblocare, mută comutatorul de confidențialitate de pe dispozitiv în poziția Microfon activat pentru a permite accesul la microfon. Consultă manualul dispozitivului ca să găsești comutatorul de confidențialitate."</string> + <string name="sensor_privacy_start_use_camera_blocked_dialog_content" msgid="7216015168047965948">"Pentru deblocare, mută comutatorul de confidențialitate de pe dispozitiv în poziția Cameră foto activată pentru a permite accesul la cameră. Consultă manualul dispozitivului ca să găsești comutatorul de confidențialitate."</string> + <string name="sensor_privacy_start_use_mic_camera_blocked_dialog_content" msgid="3960837827570483762">"Pentru deblocare, mută comutatorul de confidențialitate de pe dispozitiv în poziția Deblocat(ă) pentru a permite accesul. Consultă manualul dispozitivului ca să găsești comutatorul de confidențialitate."</string> <string name="sensor_privacy_mic_unblocked_toast_content" msgid="306555320557065068">"Microfon disponibil"</string> <string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Cameră foto disponibilă"</string> <string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Microfon și cameră disponibile"</string> <string name="media_seamless_other_device" msgid="4654849800789196737">"Alt dispozitiv"</string> - <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Comutați secțiunea Recente"</string> - <string name="zen_priority_introduction" msgid="3159291973383796646">"Se vor anunța prin sunete și vibrații numai alarmele, mementourile, evenimentele și apelanții specificați de dvs. Totuși, veți auzi tot ce alegeți să redați, inclusiv muzică, videoclipuri și jocuri."</string> - <string name="zen_alarms_introduction" msgid="3987266042682300470">"Se vor anunța prin sunete și vibrații numai alarmele. Totuși, veți auzi tot ce alegeți să redați, inclusiv muzică, videoclipuri și jocuri."</string> - <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizați"</string> - <string name="zen_silence_introduction_voice" msgid="853573681302712348">"Această opțiune blochează TOATE sunetele și vibrațiile, inclusiv cele ale alarmelor, muzicii, videoclipurilor și jocurilor. Totuși, veți putea iniția apeluri."</string> + <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Comută secțiunea Recente"</string> + <string name="zen_priority_introduction" msgid="3159291973383796646">"Se vor anunța prin sunete și vibrații numai alarmele, mementourile, evenimentele și apelanții specificați de tine. Totuși, vei auzi tot ce alegi să redai, inclusiv muzică, videoclipuri și jocuri."</string> + <string name="zen_alarms_introduction" msgid="3987266042682300470">"Se vor anunța prin sunete și vibrații numai alarmele. Totuși, vei auzi tot ce alegi să redai, inclusiv muzică, videoclipuri și jocuri."</string> + <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizează"</string> + <string name="zen_silence_introduction_voice" msgid="853573681302712348">"Această opțiune blochează TOATE sunetele și vibrațiile, inclusiv cele ale alarmelor, muzicii, videoclipurilor și jocurilor. Totuși, vei putea iniția apeluri."</string> <string name="zen_silence_introduction" msgid="6117517737057344014">"Această opțiune blochează TOATE sunetele și vibrațiile, inclusiv cele ale alarmelor, muzicii, videoclipurilor și jocurilor."</string> <string name="notification_tap_again" msgid="4477318164947497249">"Atinge din nou pentru a deschide"</string> <string name="tap_again" msgid="1315420114387908655">"Atinge din nou"</string> - <string name="keyguard_unlock" msgid="8031975796351361601">"Glisați în sus pentru a deschide"</string> + <string name="keyguard_unlock" msgid="8031975796351361601">"Glisează în sus pentru a deschide"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Apasă pictograma de deblocare pentru a deschide"</string> - <string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"S-a deblocat folosind fața. Glisați în sus și deschideți."</string> - <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"S-a deblocat cu ajutorul feței. Apasă pictograma de deblocare pentru a deschide"</string> + <string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"Deblocat folosind chipul. Glisează în sus ca să deschizi."</string> + <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Deblocat facial. Apasă pictograma Deblocare ca să deschizi."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"S-a deblocat cu ajutorul feței. Apasă pentru a deschide."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Chipul a fost recunoscut. Apasă pentru a deschide."</string> - <string name="keyguard_face_successful_unlock_press_alt_3" msgid="7219030481255573962">"Chip recunoscut. Apasă pictograma de deblocare pentru a deschide"</string> + <string name="keyguard_face_successful_unlock_press_alt_3" msgid="7219030481255573962">"Chip recunoscut. Apasă pictograma Deblocare ca să deschizi."</string> <string name="keyguard_face_successful_unlock" msgid="4203999851465708287">"S-a deblocat folosind fața"</string> <string name="keyguard_face_successful_unlock_alt1" msgid="5853906076353839628">"Chipul a fost recunoscut"</string> <string-array name="udfps_accessibility_touch_hints"> - <item msgid="1901953991150295169">"Deplasați spre stânga"</item> - <item msgid="5558598599408514296">"Deplasați în jos"</item> - <item msgid="4844142668312841831">"Deplasați spre dreapta"</item> - <item msgid="5640521437931460125">"Deplasați în sus"</item> + <item msgid="1901953991150295169">"Mută la stânga"</item> + <item msgid="5558598599408514296">"Mută în jos"</item> + <item msgid="4844142668312841831">"Mută la dreapta"</item> + <item msgid="5640521437931460125">"Mută în sus"</item> </string-array> - <string name="keyguard_retry" msgid="886802522584053523">"Glisați pentru a încerca din nou"</string> - <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Deblocați pentru a folosi NFC"</string> - <string name="do_disclosure_generic" msgid="4896482821974707167">"Dispozitivul aparține organizației dvs."</string> + <string name="keyguard_retry" msgid="886802522584053523">"Glisează pentru a încerca din nou"</string> + <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Deblochează pentru a folosi NFC"</string> + <string name="do_disclosure_generic" msgid="4896482821974707167">"Dispozitivul aparține organizației tale"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Acest dispozitiv aparține organizației <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> <string name="do_financed_disclosure_with_name" msgid="6723004643314467864">"Acest dispozitiv este oferit de <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> - <string name="phone_hint" msgid="6682125338461375925">"Glisați dinspre telefon"</string> - <string name="voice_hint" msgid="7476017460191291417">"Glisați dinspre pictogramă pentru asistentul vocal"</string> - <string name="camera_hint" msgid="4519495795000658637">"Glisați pentru a fotografia"</string> + <string name="phone_hint" msgid="6682125338461375925">"Glisează dinspre telefon"</string> + <string name="voice_hint" msgid="7476017460191291417">"Glisează dinspre pictogramă pentru asistentul vocal"</string> + <string name="camera_hint" msgid="4519495795000658637">"Glisează pentru a fotografia"</string> <string name="interruption_level_none_with_warning" msgid="8394434073508145437">"Liniște absolută. Se va opri sunetul și pentru cititoarele de ecran."</string> <string name="interruption_level_none" msgid="219484038314193379">"Niciun sunet"</string> <string name="interruption_level_priority" msgid="661294280016622209">"Numai cu prioritate"</string> @@ -342,27 +342,27 @@ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă rapid • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string> <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă lent • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string> <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string> - <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Comutați între utilizatori"</string> + <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Schimbă utilizatorul"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"meniu vertical"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string> - <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bine ați revenit în sesiunea pentru invitați!"</string> - <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vreți să continuați sesiunea?"</string> - <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Începeți din nou"</string> - <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, continuați"</string> + <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bine ai revenit în sesiunea pentru invitați!"</string> + <string name="guest_wipe_session_message" msgid="3393823610257065457">"Continui sesiunea?"</string> + <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Începe din nou"</string> + <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, continuă"</string> <string name="guest_notification_app_name" msgid="2110425506754205509">"Modul pentru invitați"</string> - <string name="guest_notification_session_active" msgid="5567273684713471450">"Folosiți modul pentru invitați"</string> - <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Dacă adăugați un utilizator nou, veți ieși din modul pentru invitați și se vor șterge toate aplicațiile și datele din sesiunea pentru invitați actuală."</string> - <string name="user_limit_reached_title" msgid="2429229448830346057">"Ați atins limita de utilizatori"</string> - <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Se poate crea doar un utilizator.}few{Puteți adăuga până la # utilizatori.}other{Puteți adăuga până la # de utilizatori.}}"</string> - <string name="user_remove_user_title" msgid="9124124694835811874">"Eliminați utilizatorul?"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Folosește modul pentru invitați"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Dacă adaugi un utilizator nou, vei ieși din modul pentru invitați și se vor șterge toate aplicațiile și datele din sesiunea actuală pentru invitați."</string> + <string name="user_limit_reached_title" msgid="2429229448830346057">"Ai atins limita de utilizatori"</string> + <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Se poate crea doar un utilizator.}few{Poți adăuga până la # utilizatori.}other{Poți adăuga până la # de utilizatori.}}"</string> + <string name="user_remove_user_title" msgid="9124124694835811874">"Excluzi utilizatorul?"</string> <string name="user_remove_user_message" msgid="6702834122128031833">"Toate aplicațiile și datele acestui utilizator vor fi șterse."</string> - <string name="user_remove_user_remove" msgid="8387386066949061256">"Eliminați"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> va avea acces la toate informațiile vizibile pe ecran sau redate pe dispozitiv în timp ce înregistrați sau proiectați. Între aceste informații se numără parole, detalii de plată, fotografii, mesaje și conținutul audio pe care îl redați."</string> - <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Serviciul care oferă această funcție va avea acces la toate informațiile vizibile pe ecran sau redate pe dispozitiv în timp ce înregistrați sau proiectați. Între aceste informații se numără parole, detalii de plată, fotografii, mesaje și conținutul audio pe care îl redați."</string> - <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Începeți să înregistrați sau să proiectați?"</string> - <string name="media_projection_dialog_title" msgid="3316063622495360646">"Începeți să înregistrați sau să proiectați cu <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> + <string name="user_remove_user_remove" msgid="8387386066949061256">"Elimină"</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> va avea acces la toate informațiile vizibile pe ecran sau redate pe dispozitiv în timp ce înregistrezi sau proiectezi. Între aceste informații se numără parole, detalii de plată, fotografii, mesaje și conținutul audio pe care îl redai."</string> + <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Serviciul care oferă această funcție va avea acces la toate informațiile vizibile pe ecran sau redate pe dispozitiv în timp ce înregistrezi sau proiectezi. Între aceste informații se numără parole, detalii de plată, fotografii, mesaje și conținutul audio pe care îl redai."</string> + <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Începi să înregistrezi sau să proiectezi?"</string> + <string name="media_projection_dialog_title" msgid="3316063622495360646">"Începi să înregistrezi sau să proiectezi cu <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> <string name="clear_all_notifications_text" msgid="348312370303046130">"Șterge toate notificările"</string> - <string name="manage_notifications_text" msgid="6885645344647733116">"Gestionați"</string> + <string name="manage_notifications_text" msgid="6885645344647733116">"Gestionează"</string> <string name="manage_notifications_history_text" msgid="57055985396576230">"Istoric"</string> <string name="notification_section_header_incoming" msgid="850925217908095197">"Noi"</string> <string name="notification_section_header_gentle" msgid="6804099527336337197">"Silențioase"</string> @@ -370,25 +370,25 @@ <string name="notification_section_header_conversations" msgid="821834744538345661">"Conversații"</string> <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Șterge toate notificările silențioase"</string> <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificări întrerupte prin „Nu deranja”"</string> - <string name="media_projection_action_text" msgid="3634906766918186440">"Începeți acum"</string> + <string name="media_projection_action_text" msgid="3634906766918186440">"Începe acum"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Nicio notificare"</string> <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Dispozitivul este gestionat de unul dintre părinți"</string> - <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organizația dvs. deține acest dispozitiv și poate monitoriza traficul de rețea"</string> + <string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organizația ta deține acest dispozitiv și poate monitoriza traficul de rețea"</string> <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> deține acest dispozitiv și poate monitoriza traficul din rețea"</string> <string name="quick_settings_financed_disclosure_named_management" msgid="2307703784594859524">"Acest dispozitiv este oferit de <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> - <string name="quick_settings_disclosure_management_named_vpn" msgid="4137564460025113168">"Acest dispozitiv aparține organizației dvs. și este conectat la internet prin aplicația <xliff:g id="VPN_APP">%1$s</xliff:g>."</string> + <string name="quick_settings_disclosure_management_named_vpn" msgid="4137564460025113168">"Acest dispozitiv aparține organizației tale și e conectat la internet prin <xliff:g id="VPN_APP">%1$s</xliff:g>."</string> <string name="quick_settings_disclosure_named_management_named_vpn" msgid="2169227918166358741">"Acest dispozitiv aparține organizației <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> și e conectat la internet prin <xliff:g id="VPN_APP">%2$s</xliff:g>."</string> - <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Dispozitivul aparține organizației dvs."</string> + <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Dispozitivul aparține organizației tale"</string> <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Acest dispozitiv aparține organizației <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string> - <string name="quick_settings_disclosure_management_vpns" msgid="929181757984262902">"Acest dispozitiv aparține organizației dvs. și este conectat la internet prin rețele VPN."</string> + <string name="quick_settings_disclosure_management_vpns" msgid="929181757984262902">"Acest dispozitiv aparține organizației tale și e conectat la internet prin VPN-uri."</string> <string name="quick_settings_disclosure_named_management_vpns" msgid="3312645578322079185">"Acest dispozitiv aparține organizației <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> și este conectat la internet prin rețele VPN."</string> - <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Este posibil ca organizația dvs. să monitorizeze traficul de rețea în profilul dvs. de serviciu"</string> - <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Este posibil ca <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> să monitorizeze traficul de rețea din profilul dvs. de serviciu"</string> + <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"E posibil ca organizația ta să monitorizeze traficul de rețea în profilul de serviciu"</string> + <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"E posibil ca <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> să monitorizeze traficul de rețea din profilul tău de serviciu"</string> <string name="quick_settings_disclosure_managed_profile_network_activity" msgid="2636594621387832827">"Adminul IT poate vedea profilul de serviciu"</string> <string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Este posibil ca rețeaua să fie monitorizată"</string> <string name="quick_settings_disclosure_vpns" msgid="3586175303518266301">"Acest dispozitiv este conectat la internet prin rețele VPN."</string> - <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="153393105176944100">"Aplicațiile dvs. pentru lucru sunt conectate la internet prin <xliff:g id="VPN_APP">%1$s</xliff:g>"</string> - <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="451254750289172191">"Aplicațiile dvs. personale sunt conectate la internet prin aplicația <xliff:g id="VPN_APP">%1$s</xliff:g>."</string> + <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="153393105176944100">"Aplicațiile pentru lucru sunt conectate la internet prin <xliff:g id="VPN_APP">%1$s</xliff:g>"</string> + <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="451254750289172191">"Aplicațiile personale sunt conectate la internet prin <xliff:g id="VPN_APP">%1$s</xliff:g>."</string> <string name="quick_settings_disclosure_named_vpn" msgid="6191822916936028208">"Acest dispozitiv este conectat la internet prin aplicația <xliff:g id="VPN_APP">%1$s</xliff:g>."</string> <string name="monitoring_title_financed_device" msgid="3659962357973919387">"Acest dispozitiv este oferit de <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> <string name="monitoring_title_device_owned" msgid="7029691083837606324">"Gestionarea dispozitivului"</string> @@ -397,18 +397,18 @@ <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"Certificate CA"</string> <string name="monitoring_button_view_policies" msgid="3869724835853502410">"Afișează politicile"</string> <string name="monitoring_button_view_controls" msgid="8316440345340701117">"Vezi opțiunile"</string> - <string name="monitoring_description_named_management" msgid="505833016545056036">"Dispozitivul aparține organizației <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nAdministratorul dvs. IT poate să monitorizeze și să gestioneze setările, accesul la nivelul companiei, aplicațiile, datele asociate dispozitivului și informațiile despre locația dispozitivului.\n\nPentru mai multe informații, contactați administratorul IT."</string> - <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"Este posibil ca <xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> să acceseze date asociate dispozitivului, să gestioneze aplicații și să modifice setările acestuia.\n\nDacă aveți întrebări, luați legătura cu <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>."</string> - <string name="monitoring_description_management" msgid="4308879039175729014">"Dispozitivul aparține organizației dvs.\n\nAdministratorul dvs. IT poate să monitorizeze și să gestioneze setările, accesul la nivelul companiei, aplicațiile, datele asociate dispozitivului și informațiile despre locația dispozitivului.\n\nPentru mai multe informații, contactați administratorul IT."</string> - <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organizația dvs. a instalat un certificat CA pe acest dispozitiv. Traficul dvs. sigur de rețea poate fi monitorizat sau modificat."</string> - <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizația dvs. a instalat un certificat CA în profilul dvs. de serviciu. Traficul dvs. sigur de rețea poate fi monitorizat sau modificat."</string> - <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Pe acest dispozitiv este instalat un certificat CA. Traficul dvs. sigur de rețea poate fi monitorizat sau modificat."</string> - <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratorul dvs. a activat înregistrarea în jurnal pentru rețea, funcție ce monitorizează traficul de pe dispozitivul dvs."</string> - <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratorul a activat înregistrarea în jurnal pentru rețea, funcție ce monitorizează traficul în profilul dvs. de serviciu, dar nu și în profilul personal."</string> + <string name="monitoring_description_named_management" msgid="505833016545056036">"Dispozitivul aparține organizației <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nAdministratorul IT poate să monitorizeze și să gestioneze setările, accesul la nivelul companiei, aplicațiile, datele asociate dispozitivului și informațiile despre locația dispozitivului.\n\nPentru mai multe informații, contactează administratorul IT."</string> + <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"E posibil ca <xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> să acceseze date asociate dispozitivului, să gestioneze aplicații și să modifice setările acestuia.\n\nDacă ai întrebări, ia legătura cu <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>."</string> + <string name="monitoring_description_management" msgid="4308879039175729014">"Dispozitivul aparține organizației tale.\n\nAdministratorul IT poate să monitorizeze și să gestioneze setările, accesul la nivelul companiei, aplicațiile, datele asociate dispozitivului și informațiile despre locația dispozitivului.\n\nPentru mai multe informații, contactează administratorul IT."</string> + <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organizația ta a instalat un certificat CA pe acest dispozitiv. Traficul de rețea securizat poate fi monitorizat sau modificat."</string> + <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizația ta a instalat un certificat CA în profilul tău de serviciu. Traficul de rețea securizat poate fi monitorizat sau modificat."</string> + <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Pe acest dispozitiv este instalat un certificat CA. Traficul de rețea securizat poate fi monitorizat sau modificat."</string> + <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratorul tău a activat înregistrarea în jurnal pentru rețea, funcție care monitorizează traficul de pe dispozitivul tău."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratorul a activat înregistrarea în jurnal pentru rețea, funcție care monitorizează traficul în profilul de serviciu, dar nu și în profilul personal."</string> <string name="monitoring_description_named_vpn" msgid="7502657784155456414">"Acest dispozitiv este conectat la internet prin aplicația <xliff:g id="VPN_APP">%1$s</xliff:g>. Activitatea în rețea, inclusiv e-mailurile și datele de navigare, sunt vizibile pentru administratorul IT."</string> <string name="monitoring_description_two_named_vpns" msgid="6726394451199620634">"Acest dispozitiv este conectat la internet prin aplicațiile <xliff:g id="VPN_APP_0">%1$s</xliff:g> și <xliff:g id="VPN_APP_1">%2$s</xliff:g>. Activitatea în rețea, inclusiv e-mailurile și datele de navigare, sunt vizibile pentru administratorul IT."</string> - <string name="monitoring_description_managed_profile_named_vpn" msgid="7254359257263069766">"Aplicațiile dvs. pentru lucru sunt conectate la internet prin aplicația <xliff:g id="VPN_APP">%1$s</xliff:g>. Activitatea în rețea cu aplicațiile pentru lucru, inclusiv e-mailurile și datele de navigare, sunt vizibile pentru administratorul IT și pentru furnizorul de servicii VPN."</string> - <string name="monitoring_description_personal_profile_named_vpn" msgid="5083909710727365452">"Aplicațiile dvs. personale sunt conectate la internet prin aplicația <xliff:g id="VPN_APP">%1$s</xliff:g>. Activitatea în rețea, inclusiv e-mailurile și datele de navigare, sunt vizibile pentru furnizorul de servicii VPN."</string> + <string name="monitoring_description_managed_profile_named_vpn" msgid="7254359257263069766">"Aplicațiile pentru lucru sunt conectate la internet prin <xliff:g id="VPN_APP">%1$s</xliff:g>. Activitatea în rețea cu aplicațiile pentru lucru, inclusiv e-mailurile și datele de navigare, sunt vizibile pentru administratorul IT și pentru furnizorul de servicii VPN."</string> + <string name="monitoring_description_personal_profile_named_vpn" msgid="5083909710727365452">"Aplicațiile personale sunt conectate la internet prin <xliff:g id="VPN_APP">%1$s</xliff:g>. Activitatea în rețea, inclusiv e-mailurile și datele de navigare, sunt vizibile pentru furnizorul de servicii VPN."</string> <string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string> <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"Deschide Setări VPN"</string> <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Dispozitivul este gestionat de unul dintre părinți. Părintele poate să vadă și să gestioneze informații cum ar fi aplicațiile pe care le folosești, locația ta și durata de folosire a dispozitivului."</string> @@ -419,21 +419,21 @@ <string name="volume_odi_captions_tip" msgid="8825655463280990941">"Adaugă subtitrări automate la fișierele media"</string> <string name="accessibility_volume_close_odi_captions_tip" msgid="8924753283621160480">"Sfat pentru subtitrări"</string> <string name="volume_odi_captions_content_description" msgid="4172765742046013630">"Suprapunere pe subtitrări"</string> - <string name="volume_odi_captions_hint_enable" msgid="2073091194012843195">"activați"</string> - <string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"dezactivați"</string> + <string name="volume_odi_captions_hint_enable" msgid="2073091194012843195">"activează"</string> + <string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"dezactivează"</string> <string name="sound_settings" msgid="8874581353127418308">"Sunete și vibrații"</string> <string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Setări"</string> <string name="screen_pinning_title" msgid="9058007390337841305">"Aplicația este fixată"</string> - <string name="screen_pinning_description" msgid="8699395373875667743">"Astfel rămâne afișat până anulați fixarea. Atingeți lung opțiunile Înapoi și Recente pentru a anula fixarea."</string> - <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Astfel rămâne afișat până anulați fixarea. Atingeți lung opțiunile Înapoi și Acasă pentru a anula fixarea."</string> - <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Astfel rămâne afișată până anulați fixarea. Glisați în sus și țineți apăsat pentru a anula fixarea."</string> - <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Astfel rămâne afișat până anulați fixarea. Atinge lung opțiunea Recente pentru a anula fixarea."</string> - <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Astfel rămâne afișat până anulați fixarea. Atingeți lung opțiunea Acasă pentru a anula fixarea."</string> + <string name="screen_pinning_description" msgid="8699395373875667743">"Astfel rămâne afișat până anulezi fixarea. Atinge lung opțiunile Înapoi și Recente pentru a anula fixarea."</string> + <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Astfel rămâne afișat până anulezi fixarea. Atinge lung opțiunile Înapoi și Acasă pentru a anula fixarea."</string> + <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Astfel rămâne afișată până anulezi fixarea. Glisează în sus și ține apăsat pentru a anula fixarea."</string> + <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Astfel rămâne afișat până anulezi fixarea. Atinge lung opțiunea Recente pentru a anula fixarea."</string> + <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Astfel rămâne afișat până anulezi fixarea. Atinge lung opțiunea Acasă pentru a anula fixarea."</string> <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Pot fi accesate date cu caracter personal (cum ar fi agenda și conținutul e-mailurilor)."</string> <string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"Aplicațiile fixate pot deschide alte aplicații."</string> - <string name="screen_pinning_toast" msgid="8177286912533744328">"Pentru a anula fixarea acestei aplicații, atingeți lung butoanele Înapoi și Recente"</string> - <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Pentru a anula fixarea acestei aplicații, atingeți lung butoanele Înapoi și Acasă"</string> - <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Pentru a anula fixarea acestei aplicații, glisați în sus și mențineți"</string> + <string name="screen_pinning_toast" msgid="8177286912533744328">"Pentru a anula fixarea acestei aplicații, atinge lung butoanele Înapoi și Recente"</string> + <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Pentru a anula fixarea acestei aplicații, atinge lung butoanele Înapoi și Acasă"</string> + <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Pentru a anula fixarea acestei aplicații, glisează în sus și menține"</string> <string name="screen_pinning_positive" msgid="3285785989665266984">"Am înțeles"</string> <string name="screen_pinning_negative" msgid="6882816864569211666">"Nu, mulțumesc"</string> <string name="screen_pinning_start" msgid="7483998671383371313">"Aplicație fixată"</string> @@ -449,57 +449,57 @@ <string name="stream_accessibility" msgid="3873610336741987152">"Accesibilitate"</string> <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Sonerie"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrații"</string> - <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Blocați"</string> + <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Blochează"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Atinge pentru a activa sunetul."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Atinge pentru a seta vibrarea. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Atinge pentru a dezactiva sunetul. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Atinge pentru a seta pe vibrații."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Atinge pentru a dezactiva sunetul."</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Atinge pentru a schimba modul soneriei"</string> - <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"dezactivați sunetul"</string> - <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activați sunetul"</string> + <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"dezactivează sunetul"</string> + <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activează sunetul"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrații"</string> <string name="volume_dialog_title" msgid="6502703403483577940">"Comenzi de volum pentru %s"</string> <string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Apelurile și notificările vor suna (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string> <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string> <string name="status_bar" msgid="4357390266055077437">"Bară de stare"</string> <string name="demo_mode" msgid="263484519766901593">"Mod demonstrativ pentru IU sistem"</string> - <string name="enable_demo_mode" msgid="3180345364745966431">"Activați modul demonstrativ"</string> + <string name="enable_demo_mode" msgid="3180345364745966431">"Activează modul demonstrativ"</string> <string name="show_demo_mode" msgid="3677956462273059726">"Afișează modul demonstrativ"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"Alarmă"</string> <string name="wallet_title" msgid="5369767670735827105">"Portofel"</string> - <string name="wallet_empty_state_label" msgid="7776761245237530394">"Configurați pentru a face achiziții mai rapide și mai sigure cu telefonul dvs."</string> + <string name="wallet_empty_state_label" msgid="7776761245237530394">"Configurează pentru a face achiziții mai rapide și mai sigure cu telefonul"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Afișează-le pe toate"</string> <string name="wallet_secondary_label_no_card" msgid="8488069304491125713">"Atinge pentru a deschide"</string> <string name="wallet_secondary_label_updating" msgid="5726130686114928551">"Se actualizează"</string> - <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Deblocați pentru a folosi"</string> + <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Deblochează pentru a folosi"</string> <string name="wallet_error_generic" msgid="257704570182963611">"A apărut o problemă la preluarea cardurilor. Încearcă din nou mai târziu"</string> <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Setările ecranului de blocare"</string> - <string name="qr_code_scanner_title" msgid="5290201053875420785">"Scanați codul QR"</string> + <string name="qr_code_scanner_title" msgid="5290201053875420785">"Scanează codul QR"</string> <string name="status_bar_work" msgid="5238641949837091056">"Profil de serviciu"</string> <string name="status_bar_airplane" msgid="4848702508684541009">"Mod Avion"</string> - <string name="zen_alarm_warning" msgid="7844303238486849503">"Nu veți auzi următoarea alarmă <xliff:g id="WHEN">%1$s</xliff:g>"</string> + <string name="zen_alarm_warning" msgid="7844303238486849503">"Nu vei auzi următoarea alarmă <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="alarm_template" msgid="2234991538018805736">"la <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil de serviciu"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Distractiv pentru unii, dar nu pentru toată lumea"</string> - <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner vă oferă modalități suplimentare de a ajusta și a personaliza interfața de utilizare Android. Aceste funcții experimentale pot să se schimbe, să se blocheze sau să dispară din versiunile viitoare. Continuați cu prudență."</string> - <string name="tuner_persistent_warning" msgid="230466285569307806">"Aceste funcții experimentale pot să se schimbe, să se blocheze sau să dispară din versiunile viitoare. Continuați cu prudență."</string> + <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner oferă modalități suplimentare de a ajusta și a personaliza interfața de utilizare Android. Aceste funcții experimentale pot să se schimbe, să se blocheze sau să dispară din versiunile viitoare. Continuă cu prudență."</string> + <string name="tuner_persistent_warning" msgid="230466285569307806">"Aceste funcții experimentale pot să se schimbe, să se blocheze sau să dispară din versiunile viitoare. Continuă cu prudență."</string> <string name="got_it" msgid="477119182261892069">"Am înțeles"</string> <string name="tuner_toast" msgid="3812684836514766951">"Felicitări! System UI Tuner a fost adăugat în Setări"</string> - <string name="remove_from_settings" msgid="633775561782209994">"Eliminați din Setări"</string> - <string name="remove_from_settings_prompt" msgid="551565437265615426">"Eliminați System UI Tuner din Setări și încetați utilizarea tuturor funcțiilor sale?"</string> - <string name="enable_bluetooth_title" msgid="866883307336662596">"Activați Bluetooth?"</string> - <string name="enable_bluetooth_message" msgid="6740938333772779717">"Pentru a conecta tastatura la tabletă, mai întâi trebuie să activați Bluetooth."</string> - <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activați"</string> + <string name="remove_from_settings" msgid="633775561782209994">"Elimină din Setări"</string> + <string name="remove_from_settings_prompt" msgid="551565437265615426">"Elimini System UI Tuner din Setări și încetezi utilizarea tuturor funcțiilor sale?"</string> + <string name="enable_bluetooth_title" msgid="866883307336662596">"Activezi Bluetooth?"</string> + <string name="enable_bluetooth_message" msgid="6740938333772779717">"Pentru a conecta tastatura la tabletă, mai întâi trebuie să activezi Bluetooth."</string> + <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activează"</string> <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Comenzi de gestionare a notificărilor"</string> <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activată – În funcție de chip"</string> - <string name="power_notification_controls_description" msgid="1334963837572708952">"Folosind comenzile de gestionare a notificărilor, puteți să setați un nivel de importanță de la 0 la 5 pentru notificările unei aplicații. \n\n"<b>"Nivelul 5"</b>" \n– Se afișează la începutul listei de notificări \n– Se permite întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 4"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 3"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n\n"<b>"Nivelul 2"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n\n"<b>"Nivelul 1"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n– Se ascunde în ecranul de blocare și în bara de stare \n– Se afișează la finalul listei de notificări \n\n"<b>"Nivelul 0"</b>" \n– Se blochează toate notificările din aplicație"</string> + <string name="power_notification_controls_description" msgid="1334963837572708952">"Folosind comenzile de gestionare a notificărilor, poți seta un nivel de importanță de la 0 la 5 pentru notificările unei aplicații. \n\n"<b>"Nivelul 5"</b>" \n– Se afișează la începutul listei de notificări \n– Se permite întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 4"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 3"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n\n"<b>"Nivelul 2"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n\n"<b>"Nivelul 1"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n– Se ascunde în ecranul de blocare și în bara de stare \n– Se afișează la finalul listei de notificări \n\n"<b>"Nivelul 0"</b>" \n– Se blochează toate notificările din aplicație"</string> <string name="inline_done_button" msgid="6043094985588909584">"Gata"</string> - <string name="inline_ok_button" msgid="603075490581280343">"Aplicați"</string> - <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Dezactivați notificările"</string> + <string name="inline_ok_button" msgid="603075490581280343">"Aplică"</string> + <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Dezactivează notificările"</string> <string name="notification_silence_title" msgid="8608090968400832335">"Silențios"</string> <string name="notification_alert_title" msgid="3656229781017543655">"Prestabilite"</string> <string name="notification_automatic_title" msgid="3745465364578762652">"Automat"</string> @@ -507,7 +507,7 @@ <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Fără sunet sau vibrații și apare în partea de jos a secțiunii de conversație"</string> <string name="notification_channel_summary_default" msgid="3282930979307248890">"Poate să sune sau să vibreze, în funcție de setările telefonului"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Poate să sune sau să vibreze, în funcție de setările telefonului. Conversațiile din balonul <xliff:g id="APP_NAME">%1$s</xliff:g> în mod prestabilit."</string> - <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Solicitați-i sistemului să stabilească dacă această notificare este sonoră sau cu vibrații."</string> + <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Solicită-i sistemului să stabilească dacă această notificare e sonoră sau cu vibrații."</string> <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Stare:</b> promovată la prestabilită"</string> <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Stare:</b> setată ca Silențioasă"</string> <string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>Stare:</b> clasificată mai sus"</string> @@ -532,9 +532,9 @@ <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Opțiunile privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g> sunt afișate"</string> <string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Opțiunile privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g> nu sunt afișate"</string> <string name="notification_more_settings" msgid="4936228656989201793">"Mai multe setări"</string> - <string name="notification_app_settings" msgid="8963648463858039377">"Personalizați"</string> + <string name="notification_app_settings" msgid="8963648463858039377">"Personalizează"</string> <string name="notification_conversation_bubble" msgid="2242180995373949022">"Afișează balonul"</string> - <string name="notification_conversation_unbubble" msgid="6908427185031099868">"Eliminați baloanele"</string> + <string name="notification_conversation_unbubble" msgid="6908427185031099868">"Elimină baloanele"</string> <string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="6429668976593634862">"comenzile notificării"</string> <string name="notification_menu_snooze_description" msgid="4740133348901973244">"opțiuni de amânare a notificării"</string> @@ -556,12 +556,12 @@ <string name="keyboard_key_space" msgid="6980847564173394012">"Spațiu"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string> - <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Redați/Întrerupeți"</string> + <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Redă/Întrerupe"</string> <string name="keyboard_key_media_stop" msgid="1509943745250377699">"Oprește"</string> <string name="keyboard_key_media_next" msgid="8502476691227914952">"Înainte"</string> <string name="keyboard_key_media_previous" msgid="5637875709190955351">"Înapoi"</string> - <string name="keyboard_key_media_rewind" msgid="3450387734224327577">"Derulați înapoi"</string> - <string name="keyboard_key_media_fast_forward" msgid="3572444327046911822">"Derulați rapid înainte"</string> + <string name="keyboard_key_media_rewind" msgid="3450387734224327577">"Derulează înapoi"</string> + <string name="keyboard_key_media_fast_forward" msgid="3572444327046911822">"Derulează rapid înainte"</string> <string name="keyboard_key_page_up" msgid="173914303254199845">"O pagină mai sus"</string> <string name="keyboard_key_page_down" msgid="9035902490071829731">"O pagină mai jos"</string> <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Șterge"</string> @@ -570,14 +570,14 @@ <string name="keyboard_key_insert" msgid="4621692715704410493">"Inserează"</string> <string name="keyboard_key_num_lock" msgid="7209960042043090548">"Num Lock"</string> <string name="keyboard_key_numpad_template" msgid="7316338238459991821">"Tasta numerică <xliff:g id="NAME">%1$s</xliff:g>"</string> - <string name="notif_inline_reply_remove_attachment_description" msgid="7954075334095405429">"Eliminați atașamentul"</string> + <string name="notif_inline_reply_remove_attachment_description" msgid="7954075334095405429">"Elimină atașamentul"</string> <string name="keyboard_shortcut_group_system" msgid="1583416273777875970">"Sistem"</string> <string name="keyboard_shortcut_group_system_home" msgid="7465138628692109907">"Ecran de pornire"</string> <string name="keyboard_shortcut_group_system_recents" msgid="8628108256824616927">"Recente"</string> <string name="keyboard_shortcut_group_system_back" msgid="1055709713218453863">"Înapoi"</string> <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notificări"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Comenzi rapide de la tastatură"</string> - <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Schimbați aspectul tastaturii"</string> + <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Schimbă aspectul tastaturii"</string> <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Aplicații"</string> <string name="keyboard_shortcut_group_applications_assist" msgid="771606231466098742">"Asistent"</string> <string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"Browser"</string> @@ -596,7 +596,7 @@ <string name="data_saver" msgid="3484013368530820763">"Economizor de date"</string> <string name="accessibility_data_saver_on" msgid="5394743820189757731">"Economizorul de date este activat"</string> <string name="switch_bar_on" msgid="1770868129120096114">"Activat"</string> - <string name="switch_bar_off" msgid="5669805115416379556">"Dezactivați"</string> + <string name="switch_bar_off" msgid="5669805115416379556">"Dezactivează"</string> <string name="tile_unavailable" msgid="3095879009136616920">"Indisponibil"</string> <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"a afla mai multe"</string> <string name="nav_bar" msgid="4642708685386136807">"Bară de navigare"</string> @@ -606,7 +606,7 @@ <string-array name="nav_bar_buttons"> <item msgid="2681220472659720036">"Clipboard"</item> <item msgid="4795049793625565683">"Cod de tastă"</item> - <item msgid="80697951177515644">"Confirmați rotirea, comutator de la tastatură"</item> + <item msgid="80697951177515644">"Confirmă rotirea, comutator de la tastatură"</item> <item msgid="7626977989589303588">"Niciunul"</item> </string-array> <string-array name="nav_bar_layouts"> @@ -623,11 +623,11 @@ <string name="right_keycode" msgid="2480715509844798438">"Codul de taste din dreapta"</string> <string name="left_icon" msgid="5036278531966897006">"Pictograma din stânga"</string> <string name="right_icon" msgid="1103955040645237425">"Pictograma din dreapta"</string> - <string name="drag_to_add_tiles" msgid="8933270127508303672">"Țineți apăsat și trageți pentru a adăuga piese"</string> - <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Țineți apăsat și trageți pentru a rearanja piesele"</string> - <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Trageți aici pentru a elimina"</string> - <string name="drag_to_remove_disabled" msgid="933046987838658850">"Aveți nevoie de cel puțin <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> carduri"</string> - <string name="qs_edit" msgid="5583565172803472437">"Editați"</string> + <string name="drag_to_add_tiles" msgid="8933270127508303672">"Ține apăsat și trage pentru a adăuga carduri"</string> + <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Ține apăsat și trage pentru a rearanja cardurile"</string> + <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Trage aici pentru a elimina"</string> + <string name="drag_to_remove_disabled" msgid="933046987838658850">"Ai nevoie de cel puțin <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> carduri"</string> + <string name="qs_edit" msgid="5583565172803472437">"Editează"</string> <string name="tuner_time" msgid="2450785840990529997">"Oră"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Afișează orele, minutele și secundele"</item> @@ -641,8 +641,8 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Afișează pictogramele de notificare cu prioritate redusă"</string> <string name="other" msgid="429768510980739978">"Altele"</string> - <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"eliminați cardul"</string> - <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adăugați cardul la sfârșit"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"elimină cardul"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adaugă cardul la sfârșit"</string> <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mută cardul"</string> <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adaugă un card"</string> <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mută pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string> @@ -659,17 +659,17 @@ <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"alege utilizatorul"</string> <string name="data_connection_no_internet" msgid="691058178914184544">"Fără conexiune la internet"</string> <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Deschide setările <xliff:g id="ID_1">%s</xliff:g>."</string> - <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Editați ordinea setărilor."</string> + <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Editează ordinea setărilor."</string> <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Meniul de pornire"</string> <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> din <xliff:g id="ID_2">%2$d</xliff:g>"</string> <string name="tuner_lock_screen" msgid="2267383813241144544">"Ecran de blocare"</string> <string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefonul s-a oprit din cauza încălzirii"</string> <string name="thermal_shutdown_message" msgid="6142269839066172984">"Acum telefonul funcționează normal.\nAtinge pentru mai multe informații"</string> - <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefonul se încălzise prea mult și s-a oprit pentru a se răci. Acum telefonul funcționează normal.\n\nTelefonul s-ar putea încălzi prea mult dacă:\n • folosiți aplicații care consumă multe resurse (de ex., jocuri, aplicații video/de navigare);\n • descărcați/încărcați fișiere mari;\n • folosiți telefonul la temperaturi ridicate."</string> + <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefonul se încălzise prea mult și s-a oprit pentru a se răci. Acum telefonul funcționează normal.\n\nTelefonul s-ar putea încălzi prea mult dacă:\n • folosești aplicații care consumă multe resurse (de ex., jocuri, aplicații video/de navigare);\n • descarci/încarci fișiere mari;\n • folosești telefonul la temperaturi ridicate."</string> <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Vezi pașii pentru îngrijire"</string> <string name="high_temp_title" msgid="2218333576838496100">"Telefonul se încălzește"</string> - <string name="high_temp_notif_message" msgid="1277346543068257549">"Anumite funcții sunt limitate în timp ce telefonul se răcește.\nAtinge pentru mai multe informații"</string> - <string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonul va încerca automat să se răcească. Puteți folosi telefonul în continuare, dar este posibil să funcționeze mai lent.\n\nDupă ce se răcește, telefonul va funcționa normal."</string> + <string name="high_temp_notif_message" msgid="1277346543068257549">"Anumite funcții sunt limitate în timp ce telefonul se răcește.\nAtinge pentru mai multe informații."</string> + <string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonul va încerca automat să se răcească. Îl poți folosi în continuare, dar e posibil să funcționeze mai lent.\n\nDupă ce se răcește, telefonul va funcționa normal."</string> <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Vezi pașii pentru îngrijire"</string> <string name="high_temp_alarm_title" msgid="8654754369605452169">"Deconectează dispozitivul"</string> <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Dispozitivul se încălzește lângă portul de încărcare. Dacă este conectat la un încărcător sau accesoriu USB, deconectează-l și ai grijă, deoarece și cablul poate fi cald."</string> @@ -679,7 +679,7 @@ <string name="lockscreen_unlock_left" msgid="1417801334370269374">"Comanda rapidă din stânga și deblochează"</string> <string name="lockscreen_unlock_right" msgid="4658008735541075346">"Comanda rapidă din dreapta și deblochează"</string> <string name="lockscreen_none" msgid="4710862479308909198">"Niciuna"</string> - <string name="tuner_launch_app" msgid="3906265365971743305">"Lansați <xliff:g id="APP">%1$s</xliff:g>"</string> + <string name="tuner_launch_app" msgid="3906265365971743305">"Lansează <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="tuner_other_apps" msgid="7767462881742291204">"Alte aplicații"</string> <string name="tuner_circle" msgid="5270591778160525693">"Cerc"</string> <string name="tuner_plus" msgid="4130366441154416484">"Plus"</string> @@ -700,7 +700,7 @@ <string name="instant_apps_message" msgid="6112428971833011754">"Aplicația a fost deschisă fără a fi instalată."</string> <string name="instant_apps_message_with_help" msgid="1816952263531203932">"Aplicația a fost deschisă fără a fi instalată. Atinge pentru a afla mai multe."</string> <string name="app_info" msgid="5153758994129963243">"Informații aplicație"</string> - <string name="go_to_web" msgid="636673528981366511">"Accesați browserul"</string> + <string name="go_to_web" msgid="636673528981366511">"Accesează browserul"</string> <string name="mobile_data" msgid="4564407557775397216">"Date mobile"</string> <string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="mobile_carrier_text_format" msgid="8912204177152950766">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string> @@ -712,18 +712,18 @@ <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"Funcția Nu deranja a fost activată de o regulă automată sau de o aplicație."</string> <string name="running_foreground_services_title" msgid="5137313173431186685">"Aplicațiile rulează în fundal"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"Atinge pentru mai multe detalii privind bateria și utilizarea datelor"</string> - <string name="mobile_data_disable_title" msgid="5366476131671617790">"Dezactivați datele mobile?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nu veți avea acces la date sau la internet prin intermediul <xliff:g id="CARRIER">%s</xliff:g>. Internetul va fi disponibil numai prin Wi-Fi."</string> - <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operatorul dvs."</string> - <string name="touch_filtered_warning" msgid="8119511393338714836">"Deoarece o aplicație acoperă o solicitare de permisiune, Setările nu vă pot verifica răspunsul."</string> - <string name="slice_permission_title" msgid="3262615140094151017">"Permiți <xliff:g id="APP_0">%1$s</xliff:g> să afișeze porțiuni din <xliff:g id="APP_2">%2$s</xliff:g>?"</string> + <string name="mobile_data_disable_title" msgid="5366476131671617790">"Dezactivezi datele mobile?"</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nu vei avea acces la date sau la internet prin intermediul <xliff:g id="CARRIER">%s</xliff:g>. Internetul va fi disponibil numai prin Wi-Fi."</string> + <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operatorul tău"</string> + <string name="touch_filtered_warning" msgid="8119511393338714836">"Deoarece o aplicație acoperă o solicitare de permisiune, Setările nu îți pot verifica răspunsul."</string> + <string name="slice_permission_title" msgid="3262615140094151017">"Permiți ca <xliff:g id="APP_0">%1$s</xliff:g> să afișeze porțiuni din <xliff:g id="APP_2">%2$s</xliff:g>?"</string> <string name="slice_permission_text_1" msgid="6675965177075443714">"- Poate citi informații din <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="slice_permission_text_2" msgid="6758906940360746983">"- Poate efectua acțiuni în <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="slice_permission_checkbox" msgid="4242888137592298523">"Permite <xliff:g id="APP">%1$s</xliff:g> să afișeze porțiuni din orice aplicație"</string> <string name="slice_permission_allow" msgid="6340449521277951123">"Permite"</string> <string name="slice_permission_deny" msgid="6870256451658176895">"Refuz"</string> <string name="auto_saver_title" msgid="6873691178754086596">"Atinge pentru a programa Economisirea energiei"</string> - <string name="auto_saver_text" msgid="3214960308353838764">"Porniți dacă este probabil ca bateria să se descarce"</string> + <string name="auto_saver_text" msgid="3214960308353838764">"Pornește dacă e probabil ca bateria să se descarce"</string> <string name="no_auto_saver_action" msgid="7467924389609773835">"Nu, mulțumesc"</string> <string name="heap_dump_tile_name" msgid="2464189856478823046">"Extrage memoria SysUI"</string> <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"În uz"</string> @@ -745,25 +745,25 @@ <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Standby"</string> <string name="magnification_window_title" msgid="4863914360847258333">"Fereastra de mărire"</string> <string name="magnification_controls_title" msgid="8421106606708891519">"Comenzi pentru fereastra de mărire"</string> - <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Măriți"</string> - <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Micșorați"</string> - <string name="accessibility_control_move_up" msgid="6622825494014720136">"Deplasați în sus"</string> - <string name="accessibility_control_move_down" msgid="5390922476900974512">"Deplasați în jos"</string> - <string name="accessibility_control_move_left" msgid="8156206978511401995">"Deplasați spre stânga"</string> - <string name="accessibility_control_move_right" msgid="8926821093629582888">"Deplasați spre dreapta"</string> + <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Mărește"</string> + <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Micșorează"</string> + <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mută în sus"</string> + <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mută în jos"</string> + <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mută la stânga"</string> + <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mută spre dreapta"</string> <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Comutator de mărire"</string> - <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Măriți tot ecranul"</string> - <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Măriți o parte a ecranului"</string> + <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Mărește tot ecranul"</string> + <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Mărește o parte a ecranului"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Comutator"</string> - <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Atingeți pentru a deschide funcțiile de accesibilitate. Personalizați sau înlocuiți butonul în Setări.\n\n"<annotation id="link">"Afișați setările"</annotation></string> + <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Atinge ca să deschizi funcțiile de accesibilitate. Personalizează sau înlocuiește butonul în setări.\n\n"<annotation id="link">"Vezi setările"</annotation></string> <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mută butonul spre margine pentru a-l ascunde temporar"</string> <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mută în stânga sus"</string> <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mută în dreapta sus"</string> <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mută în stânga jos"</string> <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mută în dreapta jos"</string> - <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mutați în afară și ascundeți"</string> - <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mutați în afară și afișați"</string> - <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"Activați / dezactivați"</string> + <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mută la margine și ascunde"</string> + <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mută de la margine și afișează"</string> + <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"Activează / dezactivează"</string> <string name="quick_controls_title" msgid="6839108006171302273">"Comenzile dispozitivelor"</string> <string name="controls_providers_title" msgid="6879775889857085056">"Alege aplicația pentru a adăuga comenzi"</string> <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{S-a adăugat # comandă.}few{S-au adăugat # comenzi.}other{S-au adăugat # de comenzi.}}"</string> @@ -771,83 +771,83 @@ <string name="accessibility_control_favorite" msgid="8694362691985545985">"Marcată ca preferată"</string> <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Marcată ca preferată, poziția <xliff:g id="NUMBER">%d</xliff:g>"</string> <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"S-a anulat marcarea ca preferată"</string> - <string name="accessibility_control_change_favorite" msgid="2943178027582253261">"marcați ca preferată"</string> - <string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"anulați marcarea ca preferată"</string> + <string name="accessibility_control_change_favorite" msgid="2943178027582253261">"marchează ca preferată"</string> + <string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"anulează marcarea ca preferată"</string> <string name="accessibility_control_move" msgid="8980344493796647792">"Mută pe poziția <xliff:g id="NUMBER">%d</xliff:g>"</string> <string name="controls_favorite_default_title" msgid="967742178688938137">"Comenzi"</string> <string name="controls_favorite_subtitle" msgid="6481675111056961083">"Alege comenzile de accesat din Setările rapide"</string> - <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Țineți apăsat și trageți pentru a rearanja comenzile"</string> + <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Ține apăsat și trage pentru a rearanja comenzile"</string> <string name="controls_favorite_removed" msgid="5276978408529217272">"Au fost șterse toate comenzile"</string> <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Modificările nu au fost salvate"</string> <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Vezi alte aplicații"</string> - <string name="controls_favorite_load_error" msgid="5126216176144877419">"Comenzile nu au putut fi încărcate. Accesați aplicația <xliff:g id="APP">%s</xliff:g> pentru a vă asigura că setările aplicației nu s-au schimbat."</string> + <string name="controls_favorite_load_error" msgid="5126216176144877419">"Comenzile nu au putut fi încărcate. Accesează aplicația <xliff:g id="APP">%s</xliff:g> pentru a te asigura că setările aplicației nu s-au schimbat."</string> <string name="controls_favorite_load_none" msgid="7687593026725357775">"Nu sunt disponibile comenzi compatibile"</string> <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Altul"</string> <string name="controls_dialog_title" msgid="2343565267424406202">"Adaugă la comenzile dispozitivelor"</string> <string name="controls_dialog_ok" msgid="2770230012857881822">"Adaugă"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Sugerat de <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_tile_locked" msgid="731547768182831938">"Dispozitiv blocat"</string> - <string name="controls_settings_show_controls_dialog_title" msgid="3357852503553809554">"Vedeți și controlați dispozitivele de pe ecranul de blocare?"</string> - <string name="controls_settings_show_controls_dialog_message" msgid="7666211700524587969">"Puteți adăuga comenzi pentru dispozitivele externe pe ecranul de blocare.\n\nAplicația de pe dispozitiv vă poate da posibilitatea să controlați unele dispozitive fără să deblocați telefonul.\n\nPuteți face modificări oricând în Setări."</string> - <string name="controls_settings_trivial_controls_dialog_title" msgid="7593188157655036677">"Controlați dispozitivele de pe ecranul de blocare?"</string> - <string name="controls_settings_trivial_controls_dialog_message" msgid="237183787721917586">"Puteți să controlați unele dispozitive fără să deblocați telefonul sau tableta.\n\nAplicația de pe dispozitiv stabilește dispozitivele care pot fi controlate astfel."</string> + <string name="controls_settings_show_controls_dialog_title" msgid="3357852503553809554">"Afișezi și controlezi dispozitivele de pe ecranul de blocare?"</string> + <string name="controls_settings_show_controls_dialog_message" msgid="7666211700524587969">"Poți adăuga comenzi pentru dispozitivele externe pe ecranul de blocare.\n\nAplicația de pe dispozitiv îți poate permite să controlezi unele dispozitive fără să deblochezi telefonul.\n\nPoți face modificări oricând în setări."</string> + <string name="controls_settings_trivial_controls_dialog_title" msgid="7593188157655036677">"Controlezi dispozitivele de pe ecranul de blocare?"</string> + <string name="controls_settings_trivial_controls_dialog_message" msgid="237183787721917586">"Poți controla unele dispozitive fără să deblochezi telefonul sau tableta.\n\nAplicația de pe dispozitiv stabilește dispozitivele care pot fi controlate astfel."</string> <string name="controls_settings_dialog_neutral_button" msgid="4514446354793124140">"Nu, mulțumesc"</string> <string name="controls_settings_dialog_positive_button" msgid="436070672551674863">"Da"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Codul PIN conține litere sau simboluri"</string> - <string name="controls_pin_verify" msgid="3452778292918877662">"Verificați <xliff:g id="DEVICE">%s</xliff:g>"</string> + <string name="controls_pin_verify" msgid="3452778292918877662">"Verifică <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Cod PIN greșit"</string> <string name="controls_pin_instructions" msgid="6363309783822475238">"Introdu codul PIN"</string> <string name="controls_pin_instructions_retry" msgid="1566667581012131046">"Încearcă alt cod PIN"</string> - <string name="controls_confirmation_message" msgid="7744104992609594859">"Confirmați schimbarea pentru <xliff:g id="DEVICE">%s</xliff:g>"</string> - <string name="controls_structure_tooltip" msgid="4355922222944447867">"Glisați pentru a vedea mai multe"</string> + <string name="controls_confirmation_message" msgid="7744104992609594859">"Confirmă schimbarea pentru <xliff:g id="DEVICE">%s</xliff:g>"</string> + <string name="controls_structure_tooltip" msgid="4355922222944447867">"Glisează pentru a vedea mai multe"</string> <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Se încarcă recomandările"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> - <string name="controls_media_close_session" msgid="4780485355795635052">"Ascundeți comanda media pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="controls_media_close_session" msgid="4780485355795635052">"Ascunzi comanda media pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="controls_media_active_session" msgid="3146882316024153337">"Sesiunea media actuală nu se poate ascunde."</string> <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ascunde"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Reia"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Setări"</string> <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> de la <xliff:g id="ARTIST_NAME">%2$s</xliff:g> se redă în <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string> <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> din <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string> - <string name="controls_media_button_play" msgid="2705068099607410633">"Redați"</string> - <string name="controls_media_button_pause" msgid="8614887780950376258">"Întrerupeți"</string> + <string name="controls_media_button_play" msgid="2705068099607410633">"Redă"</string> + <string name="controls_media_button_pause" msgid="8614887780950376258">"Întrerupe"</string> <string name="controls_media_button_prev" msgid="8126822360056482970">"Melodia anterioară"</string> <string name="controls_media_button_next" msgid="6662636627525947610">"Melodia următoare"</string> <string name="controls_media_button_connecting" msgid="3138354625847598095">"Se conectează"</string> - <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"Redați"</string> + <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"Redă"</string> <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Deschide <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string> - <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Redați <xliff:g id="SONG_NAME">%1$s</xliff:g> de la <xliff:g id="ARTIST_NAME">%2$s</xliff:g> în <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string> - <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Redați <xliff:g id="SONG_NAME">%1$s</xliff:g> în <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string> + <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Redă <xliff:g id="SONG_NAME">%1$s</xliff:g> de la <xliff:g id="ARTIST_NAME">%2$s</xliff:g> în <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string> + <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Redă <xliff:g id="SONG_NAME">%1$s</xliff:g> în <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string> <string name="media_transfer_undo" msgid="1895606387620728736">"Anulează"</string> - <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Apropiați-vă pentru a reda pe <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string> - <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Mergeți mai aproape de <xliff:g id="DEVICENAME">%1$s</xliff:g> ca să redați acolo"</string> + <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Apropie-te pentru a reda pe <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string> + <string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Apropie-te de <xliff:g id="DEVICENAME">%1$s</xliff:g> ca să redai acolo"</string> <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Se redă pe <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string> <string name="media_transfer_failed" msgid="7955354964610603723">"A apărut o eroare. Încearcă din nou."</string> - <string name="controls_error_timeout" msgid="794197289772728958">"Inactiv, verificați aplicația"</string> + <string name="controls_error_timeout" msgid="794197289772728958">"Inactiv, verifică aplicația"</string> <string name="controls_error_removed" msgid="6675638069846014366">"Nu s-a găsit"</string> <string name="controls_error_removed_title" msgid="1207794911208047818">"Comanda este indisponibilă"</string> - <string name="controls_error_removed_message" msgid="2885911717034750542">"Nu s-a putut accesa <xliff:g id="DEVICE">%1$s</xliff:g>. Accesați aplicația <xliff:g id="APPLICATION">%2$s</xliff:g> pentru a vă asigura de disponibilitatea comenzii și că setările aplicației nu s-au schimbat."</string> + <string name="controls_error_removed_message" msgid="2885911717034750542">"Nu s-a putut accesa <xliff:g id="DEVICE">%1$s</xliff:g>. Accesează aplicația <xliff:g id="APPLICATION">%2$s</xliff:g> pentru a te asigura de disponibilitatea comenzii și că setările aplicației nu s-au schimbat."</string> <string name="controls_open_app" msgid="483650971094300141">"Deschide aplicația"</string> <string name="controls_error_generic" msgid="352500456918362905">"Starea nu se poate încărca"</string> - <string name="controls_error_failed" msgid="960228639198558525">"Eroare, încercați din nou"</string> + <string name="controls_error_failed" msgid="960228639198558525">"Eroare, încearcă din nou"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Adaugă comenzi"</string> - <string name="controls_menu_edit" msgid="890623986951347062">"Editați comenzile"</string> + <string name="controls_menu_edit" msgid="890623986951347062">"Editează comenzile"</string> <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Adaugă ieșiri"</string> <string name="media_output_dialog_group" msgid="5571251347877452212">"Grup"</string> <string name="media_output_dialog_single_device" msgid="3102758980643351058">"S-a selectat un dispozitiv"</string> <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"S-au selectat <xliff:g id="COUNT">%1$d</xliff:g> dispozitive"</string> <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(deconectat)"</string> <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nu se poate comuta. Atinge pentru a încerca din nou."</string> - <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Conectați un dispozitiv"</string> - <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Pentru a proiecta această sesiune, deschideți aplicația."</string> + <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Conectează un dispozitiv"</string> + <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Pentru a proiecta această sesiune, deschide aplicația."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplicație necunoscută"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Nu mai proiectați"</string> + <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Nu mai proiecta"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispozitive disponibile pentru ieșire audio."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volum"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cum funcționează transmisia"</string> - <string name="media_output_broadcast" msgid="3555580945878071543">"Transmiteți"</string> - <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Persoanele din apropiere cu dispozitive Bluetooth compatibile pot asculta conținutul pe care îl transmiteți"</string> - <string name="media_output_broadcasting_message" msgid="4150299923404886073">"Ca să asculte transmisia dvs., persoanele din apropiere cu dispozitive Bluetooth compatibile vă pot scana codul QR sau pot folosi numele și parola transmisiei."</string> + <string name="media_output_broadcast" msgid="3555580945878071543">"Transmite"</string> + <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Persoanele din apropiere cu dispozitive Bluetooth compatibile pot asculta conținutul pe care îl transmiți"</string> + <string name="media_output_broadcasting_message" msgid="4150299923404886073">"Ca să-ți asculte transmisia, persoanele din apropiere cu dispozitive Bluetooth compatibile pot să îți scaneze codul QR sau să folosească numele și parola transmisiei."</string> <string name="media_output_broadcast_name" msgid="8786127091542624618">"Numele transmisiei"</string> <string name="media_output_broadcast_code" msgid="870795639644728542">"Parolă"</string> <string name="media_output_broadcast_dialog_save" msgid="7910865591430010198">"Salvează"</string> @@ -859,8 +859,8 @@ <string name="build_number_copy_toast" msgid="877720921605503046">"Numărul versiunii s-a copiat în clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Deschide conversația"</string> <string name="select_conversation_title" msgid="6716364118095089519">"Widgeturi pentru conversație"</string> - <string name="select_conversation_text" msgid="3376048251434956013">"Atingeți o conversație ca să o adăugați pe ecranul de pornire"</string> - <string name="no_conversations_text" msgid="5354115541282395015">"Conversațiile dvs. recente se vor afișa aici"</string> + <string name="select_conversation_text" msgid="3376048251434956013">"Atinge o conversație ca să o adaugi pe ecranul de pornire"</string> + <string name="no_conversations_text" msgid="5354115541282395015">"Conversațiile recente se vor afișa aici"</string> <string name="priority_conversations" msgid="3967482288896653039">"Conversații cu prioritate"</string> <string name="recent_conversations" msgid="8531874684782574622">"Conversații recente"</string> <string name="days_timestamp" msgid="5821854736213214331">"Acum <xliff:g id="DURATION">%1$s</xliff:g> zile"</string> @@ -897,10 +897,10 @@ <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Atinge pentru mai multe informații"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nicio alarmă setată"</string> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor de amprentă"</string> - <string name="accessibility_authenticate_hint" msgid="798914151813205721">"Autentificați-vă"</string> - <string name="accessibility_enter_hint" msgid="2617864063504824834">"Accesați dispozitivul"</string> - <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Folosiți amprenta ca să deschideți"</string> - <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentificare obligatorie. Atingeți senzorul de amprentă pentru a vă autentifica."</string> + <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentifică-te"</string> + <string name="accessibility_enter_hint" msgid="2617864063504824834">"Accesează dispozitivul"</string> + <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Folosește amprenta ca să deschizi"</string> + <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentificare obligatorie. Atinge senzorul de amprentă pentru a te autentifica."</string> <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Apel telefonic în desfășurare"</string> <string name="mobile_data_settings_title" msgid="3955246641380064901">"Date mobile"</string> <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string> @@ -910,31 +910,31 @@ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nu sunt disponibile alte rețele"</string> <string name="all_network_unavailable" msgid="4112774339909373349">"Nicio rețea disponibilă"</string> <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string> - <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Atingeți o rețea pentru a vă conecta"</string> - <string name="unlock_to_view_networks" msgid="5072880496312015676">"Deblocați pentru a vedea rețelele"</string> + <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Atinge o rețea pentru a te conecta"</string> + <string name="unlock_to_view_networks" msgid="5072880496312015676">"Deblochează pentru a vedea rețelele"</string> <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Se caută rețele…"</string> <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nu s-a realizat conexiunea la rețea"</string> <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Deocamdată, Wi-Fi nu se poate conecta automat"</string> <string name="see_all_networks" msgid="3773666844913168122">"Afișează-le pe toate"</string> - <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pentru a schimba rețeaua, deconectați ethernet"</string> - <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Pentru a îmbunătăți experiența cu dispozitivul, aplicațiile și serviciile pot să caute în continuare rețele Wi‑Fi chiar și atunci când conexiunea Wi-Fi este dezactivată. Puteți să schimbați acest aspect din setările pentru căutarea de rețele Wi-Fi. "<annotation id="link">"Schimbați"</annotation></string> - <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Dezactivați modul Avion"</string> + <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pentru a schimba rețeaua, deconectează ethernet"</string> + <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Pentru a îmbunătăți experiența cu dispozitivul, aplicațiile și serviciile pot să caute în continuare rețele Wi‑Fi chiar și atunci când conexiunea Wi-Fi e dezactivată. Poți schimba opțiunea din setările pentru căutarea de rețele Wi-Fi. "<annotation id="link">"Schimbă"</annotation></string> + <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Dezactivează modul Avion"</string> <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vrea să adauge următorul card la Setări rapide"</string> - <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adaugă un card"</string> - <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nu adăugați un card"</string> + <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adaugă cardul"</string> + <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nu adăuga cardul"</string> <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Alege utilizatorul"</string> <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplicație este activă}few{# aplicații sunt active}other{# de aplicații sunt active}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Informații noi"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplicații active"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Aceste aplicații sunt active și rulează, chiar dacă nu le folosiți. Astfel, funcțiile lor sunt îmbunătățite, dar autonomia bateriei poate fi afectată."</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Aceste aplicații sunt active și rulează, chiar dacă nu le folosești. Astfel, funcțiile lor sunt îmbunătățite, dar autonomia bateriei poate fi afectată."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Oprește"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Oprită"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Gata"</string> <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"S-a copiat"</string> <string name="clipboard_edit_source" msgid="9156488177277788029">"Din <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Închide textul copiat"</string> - <string name="clipboard_edit_text_description" msgid="805254383912962103">"Editați textul copiat"</string> - <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editați imaginea copiată"</string> + <string name="clipboard_edit_text_description" msgid="805254383912962103">"Editează textul copiat"</string> + <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editează imaginea copiată"</string> <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Trimite către un dispozitiv din apropiere"</string> <string name="clipboard_text_hidden" msgid="7926899867471812305">"Atinge pentru a afișa"</string> <string name="clipboard_text_copied" msgid="5100836834278976679">"Textul a fost copiat"</string> @@ -943,9 +943,9 @@ <string name="clipboard_editor" msgid="2971197550401892843">"Editor de clipboard"</string> <string name="clipboard_overlay_window_name" msgid="6450043652167357664">"Clipboard"</string> <string name="clipboard_image_preview" msgid="2156475174343538128">"Previzualizarea imaginii"</string> - <string name="clipboard_edit" msgid="4500155216174011640">"editați"</string> + <string name="clipboard_edit" msgid="4500155216174011640">"editează"</string> <string name="add" msgid="81036585205287996">"Adaugă"</string> - <string name="manage_users" msgid="1823875311934643849">"Gestionați utilizatorii"</string> + <string name="manage_users" msgid="1823875311934643849">"Gestionează utilizatorii"</string> <string name="drag_split_not_supported" msgid="4326847447699729722">"Notificarea nu acceptă tragerea pe ecranul împărțit."</string> <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi indisponibil"</string> <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Modul Prioritate"</string> @@ -956,10 +956,10 @@ <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificare}few{# notificări}other{# de notificări}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Se difuzează"</string> - <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Opriți difuzarea <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Dacă difuzați <xliff:g id="SWITCHAPP">%1$s</xliff:g> sau schimbați rezultatul, difuzarea actuală se va opri"</string> - <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Difuzați <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> - <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Schimbați rezultatul"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Oprești transmisia <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Dacă transmiți <xliff:g id="SWITCHAPP">%1$s</xliff:g> sau schimbi ieșirea, transmisia actuală se va opri"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmite <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Schimbă rezultatul"</string> <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Necunoscută"</string> <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EE, z LLL"</string> <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index ce512934511f..cab5c4e3b38c 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -623,7 +623,7 @@ <string name="right_keycode" msgid="2480715509844798438">"Pravý kód klávesnice"</string> <string name="left_icon" msgid="5036278531966897006">"Ľavá ikona"</string> <string name="right_icon" msgid="1103955040645237425">"Pravá ikona"</string> - <string name="drag_to_add_tiles" msgid="8933270127508303672">"Pridržaním a presunutím pridáte dlaždice"</string> + <string name="drag_to_add_tiles" msgid="8933270127508303672">"Pridržaním a presunutím pridáte karty"</string> <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Dlaždice môžete usporiadať pridržaním a presunutím"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Presunutím sem odstránite"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Minimálny počet vyžadovaných dlaždíc: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml index f4d482406fa0..b24ce122208f 100644 --- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml @@ -66,8 +66,6 @@ <dimen name="lockscreen_shade_qs_transition_distance">@dimen/lockscreen_shade_notifications_scrim_transition_distance</dimen> <dimen name="lockscreen_shade_qs_transition_delay">@dimen/lockscreen_shade_notifications_scrim_transition_delay</dimen> <dimen name="lockscreen_shade_qs_squish_transition_distance">@dimen/lockscreen_shade_qs_transition_distance</dimen> - <!-- On split-shade, the QS squish transition should start from half height. --> - <item name="lockscreen_shade_qs_squish_start_fraction" type="dimen" format="float" >0.5</item> <!-- On split-shade, there should be no depth effect, so setting the value to 0. --> <dimen name="lockscreen_shade_depth_controller_transition_distance">0dp</dimen> <dimen name="lockscreen_shade_udfps_keyguard_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index a587e5a806ca..5dcbeb5c85cf 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -86,8 +86,6 @@ <dimen name="lockscreen_shade_qs_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen> <dimen name="lockscreen_shade_qs_transition_delay">@dimen/lockscreen_shade_scrim_transition_distance</dimen> <dimen name="lockscreen_shade_qs_squish_transition_distance">@dimen/lockscreen_shade_qs_transition_distance</dimen> - <!-- On large screen portrait, the QS squish transition should start from half height. --> - <item name="lockscreen_shade_qs_squish_start_fraction" type="dimen" format="float" >0.5</item> <dimen name="lockscreen_shade_depth_controller_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen> <dimen name="lockscreen_shade_udfps_keyguard_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen> <dimen name="lockscreen_shade_status_bar_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index eb6c45747924..f7019dcd06ee 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1174,6 +1174,7 @@ <!-- Output switcher panel related dimensions --> <dimen name="media_output_dialog_list_max_height">355dp</dimen> + <dimen name="media_output_dialog_list_item_height">76dp</dimen> <dimen name="media_output_dialog_header_album_icon_size">72dp</dimen> <dimen name="media_output_dialog_header_back_icon_size">32dp</dimen> <dimen name="media_output_dialog_header_icon_padding">16dp</dimen> @@ -1221,7 +1222,7 @@ <!-- The fraction at which the QS "squish" transition should start during the lockscreen shade expansion. 0 is fully collapsed, 1 is fully expanded. --> - <item type="dimen" format="float" name="lockscreen_shade_qs_squish_start_fraction">0</item> + <item type="dimen" format="float" name="lockscreen_shade_qs_squish_start_fraction">0.5</item> <!-- Distance that the full shade transition takes in order for depth of the wallpaper to fully change. --> @@ -1454,6 +1455,8 @@ <dimen name="fgs_manager_list_top_spacing">12dp</dimen> <dimen name="media_projection_app_selector_icon_size">32dp</dimen> + <dimen name="media_projection_app_selector_recents_padding">16dp</dimen> + <dimen name="media_projection_app_selector_loader_size">32dp</dimen> <!-- Dream overlay related dimensions --> <dimen name="dream_overlay_status_bar_height">60dp</dimen> diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt index cdedc64ed0e9..97665145ce76 100644 --- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt +++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt @@ -93,8 +93,8 @@ fun View.toBitmap(window: Window? = null): Bitmap { Futures.addCallback( captureToBitmap(window), object : FutureCallback<Bitmap> { - override fun onSuccess(result: Bitmap) { - continuation.resumeWith(Result.success(result)) + override fun onSuccess(result: Bitmap?) { + continuation.resumeWith(Result.success(result!!)) } override fun onFailure(t: Throwable) { diff --git a/packages/SystemUI/shared/res/values/attrs.xml b/packages/SystemUI/shared/res/values/attrs.xml index f9d66ee583da..96a58405a764 100644 --- a/packages/SystemUI/shared/res/values/attrs.xml +++ b/packages/SystemUI/shared/res/values/attrs.xml @@ -25,4 +25,39 @@ <attr name="lockScreenWeight" format="integer" /> <attr name="chargeAnimationDelay" format="integer" /> </declare-styleable> + + <declare-styleable name="DoubleShadowAttrDeclare"> + <attr name="keyShadowBlur" format="dimension" /> + <attr name="keyShadowOffsetX" format="dimension" /> + <attr name="keyShadowOffsetY" format="dimension" /> + <attr name="keyShadowAlpha" format="float" /> + <attr name="ambientShadowBlur" format="dimension" /> + <attr name="ambientShadowOffsetX" format="dimension" /> + <attr name="ambientShadowOffsetY" format="dimension" /> + <attr name="ambientShadowAlpha" format="float" /> + </declare-styleable> + + <declare-styleable name="DoubleShadowTextClock"> + <attr name="keyShadowBlur" /> + <attr name="keyShadowOffsetX" /> + <attr name="keyShadowOffsetY" /> + <attr name="keyShadowAlpha" /> + <attr name="ambientShadowBlur" /> + <attr name="ambientShadowOffsetX" /> + <attr name="ambientShadowOffsetY" /> + <attr name="ambientShadowAlpha" /> + </declare-styleable> + + <declare-styleable name="DoubleShadowTextView"> + <attr name="keyShadowBlur" /> + <attr name="keyShadowOffsetX" /> + <attr name="keyShadowOffsetY" /> + <attr name="keyShadowAlpha" /> + <attr name="ambientShadowBlur" /> + <attr name="ambientShadowOffsetX" /> + <attr name="ambientShadowOffsetY" /> + <attr name="ambientShadowAlpha" /> + <attr name="drawableIconSize" format="dimension" /> + <attr name="drawableIconInsetSize" format="dimension" /> + </declare-styleable> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt index 835d6e92a63d..38a312448bab 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -105,7 +105,8 @@ open class ClockRegistry( ) } - pluginManager.addPluginListener(pluginListener, ClockProviderPlugin::class.java) + pluginManager.addPluginListener(pluginListener, ClockProviderPlugin::class.java, + true /* allowMultiple */) context.contentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE), false, diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java index 422274469dcc..2111df501415 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java @@ -237,6 +237,13 @@ public class Task { public ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData(); + /** + * Indicates that this task for the desktop tile in recents. + * + * Used when desktop mode feature is enabled. + */ + public boolean desktopTile; + public Task() { // Do nothing } @@ -267,6 +274,7 @@ public class Task { this(other.key, other.colorPrimary, other.colorBackground, other.isDockable, other.isLocked, other.taskDescription, other.topActivity); lastSnapshotData.set(other.lastSnapshotData); + desktopTile = other.desktopTile; } /** diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt new file mode 100644 index 000000000000..3748eba47be5 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.shared.shadow + +import android.graphics.BlendMode +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.ColorFilter +import android.graphics.PixelFormat +import android.graphics.PorterDuff +import android.graphics.PorterDuffColorFilter +import android.graphics.RenderEffect +import android.graphics.RenderNode +import android.graphics.Shader +import android.graphics.drawable.Drawable +import android.graphics.drawable.InsetDrawable +import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo + +/** A component to draw an icon with two layers of shadows. */ +class DoubleShadowIconDrawable( + keyShadowInfo: ShadowInfo, + ambientShadowInfo: ShadowInfo, + iconDrawable: Drawable, + iconSize: Int, + val iconInsetSize: Int +) : Drawable() { + private val mAmbientShadowInfo: ShadowInfo + private val mCanvasSize: Int + private val mKeyShadowInfo: ShadowInfo + private val mIconDrawable: InsetDrawable + private val mDoubleShadowNode: RenderNode + + init { + mCanvasSize = iconSize + iconInsetSize * 2 + mKeyShadowInfo = keyShadowInfo + mAmbientShadowInfo = ambientShadowInfo + setBounds(0, 0, mCanvasSize, mCanvasSize) + mIconDrawable = InsetDrawable(iconDrawable, iconInsetSize) + mIconDrawable.setBounds(0, 0, mCanvasSize, mCanvasSize) + mDoubleShadowNode = createShadowRenderNode() + } + + private fun createShadowRenderNode(): RenderNode { + val renderNode = RenderNode("DoubleShadowNode") + renderNode.setPosition(0, 0, mCanvasSize, mCanvasSize) + // Create render effects + val ambientShadow = + createShadowRenderEffect( + mAmbientShadowInfo.blur, + mAmbientShadowInfo.offsetX, + mAmbientShadowInfo.offsetY, + mAmbientShadowInfo.alpha + ) + val keyShadow = + createShadowRenderEffect( + mKeyShadowInfo.blur, + mKeyShadowInfo.offsetX, + mKeyShadowInfo.offsetY, + mKeyShadowInfo.alpha + ) + val blend = RenderEffect.createBlendModeEffect(ambientShadow, keyShadow, BlendMode.DARKEN) + renderNode.setRenderEffect(blend) + return renderNode + } + + private fun createShadowRenderEffect( + radius: Float, + offsetX: Float, + offsetY: Float, + alpha: Float + ): RenderEffect { + return RenderEffect.createColorFilterEffect( + PorterDuffColorFilter(Color.argb(alpha, 0f, 0f, 0f), PorterDuff.Mode.MULTIPLY), + RenderEffect.createOffsetEffect( + offsetX, + offsetY, + RenderEffect.createBlurEffect(radius, radius, Shader.TileMode.CLAMP) + ) + ) + } + + override fun draw(canvas: Canvas) { + if (canvas.isHardwareAccelerated) { + if (!mDoubleShadowNode.hasDisplayList()) { + // Record render node if its display list is not recorded or discarded + // (which happens when it's no longer drawn by anything). + val recordingCanvas = mDoubleShadowNode.beginRecording() + mIconDrawable.draw(recordingCanvas) + mDoubleShadowNode.endRecording() + } + canvas.drawRenderNode(mDoubleShadowNode) + } + mIconDrawable.draw(canvas) + } + + override fun getOpacity(): Int { + return PixelFormat.TRANSPARENT + } + + override fun setAlpha(alpha: Int) { + mIconDrawable.alpha = alpha + } + + override fun setColorFilter(colorFilter: ColorFilter?) { + mIconDrawable.colorFilter = colorFilter + } + + override fun setTint(color: Int) { + mIconDrawable.setTint(color) + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextClock.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextClock.kt new file mode 100644 index 000000000000..f2db129120e9 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextClock.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.shared.shadow + +import android.content.Context +import android.graphics.Canvas +import android.util.AttributeSet +import android.widget.TextClock +import com.android.systemui.shared.R +import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo +import com.android.systemui.shared.shadow.DoubleShadowTextHelper.applyShadows + +/** Extension of [TextClock] which draws two shadows on the text (ambient and key shadows) */ +class DoubleShadowTextClock +@JvmOverloads +constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + defStyleRes: Int = 0 +) : TextClock(context, attrs, defStyleAttr, defStyleRes) { + private val mAmbientShadowInfo: ShadowInfo + private val mKeyShadowInfo: ShadowInfo + + init { + val attributes = + context.obtainStyledAttributes( + attrs, + R.styleable.DoubleShadowTextClock, + defStyleAttr, + defStyleRes + ) + try { + val keyShadowBlur = + attributes.getDimensionPixelSize(R.styleable.DoubleShadowTextClock_keyShadowBlur, 0) + val keyShadowOffsetX = + attributes.getDimensionPixelSize( + R.styleable.DoubleShadowTextClock_keyShadowOffsetX, + 0 + ) + val keyShadowOffsetY = + attributes.getDimensionPixelSize( + R.styleable.DoubleShadowTextClock_keyShadowOffsetY, + 0 + ) + val keyShadowAlpha = + attributes.getFloat(R.styleable.DoubleShadowTextClock_keyShadowAlpha, 0f) + mKeyShadowInfo = + ShadowInfo( + keyShadowBlur.toFloat(), + keyShadowOffsetX.toFloat(), + keyShadowOffsetY.toFloat(), + keyShadowAlpha + ) + val ambientShadowBlur = + attributes.getDimensionPixelSize( + R.styleable.DoubleShadowTextClock_ambientShadowBlur, + 0 + ) + val ambientShadowOffsetX = + attributes.getDimensionPixelSize( + R.styleable.DoubleShadowTextClock_ambientShadowOffsetX, + 0 + ) + val ambientShadowOffsetY = + attributes.getDimensionPixelSize( + R.styleable.DoubleShadowTextClock_ambientShadowOffsetY, + 0 + ) + val ambientShadowAlpha = + attributes.getFloat(R.styleable.DoubleShadowTextClock_ambientShadowAlpha, 0f) + mAmbientShadowInfo = + ShadowInfo( + ambientShadowBlur.toFloat(), + ambientShadowOffsetX.toFloat(), + ambientShadowOffsetY.toFloat(), + ambientShadowAlpha + ) + } finally { + attributes.recycle() + } + } + + public override fun onDraw(canvas: Canvas) { + applyShadows(mKeyShadowInfo, mAmbientShadowInfo, this, canvas) { super.onDraw(canvas) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextHelper.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextHelper.kt index b1dc5a2e5dea..eaac93dc2555 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextHelper.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextHelper.kt @@ -14,31 +14,33 @@ * limitations under the License. */ -package com.android.systemui.dreams.complication +package com.android.systemui.shared.shadow import android.graphics.Canvas +import android.graphics.Color import android.widget.TextView -import androidx.annotation.ColorInt -class DoubleShadowTextHelper -constructor( - private val keyShadowInfo: ShadowInfo, - private val ambientShadowInfo: ShadowInfo, -) { +object DoubleShadowTextHelper { data class ShadowInfo( val blur: Float, val offsetX: Float = 0f, val offsetY: Float = 0f, - @ColorInt val color: Int + val alpha: Float ) - fun applyShadows(view: TextView, canvas: Canvas, onDrawCallback: () -> Unit) { + fun applyShadows( + keyShadowInfo: ShadowInfo, + ambientShadowInfo: ShadowInfo, + view: TextView, + canvas: Canvas, + onDrawCallback: () -> Unit + ) { // We enhance the shadow by drawing the shadow twice view.paint.setShadowLayer( ambientShadowInfo.blur, ambientShadowInfo.offsetX, ambientShadowInfo.offsetY, - ambientShadowInfo.color + Color.argb(ambientShadowInfo.alpha, 0f, 0f, 0f) ) onDrawCallback() canvas.save() @@ -53,7 +55,7 @@ constructor( keyShadowInfo.blur, keyShadowInfo.offsetX, keyShadowInfo.offsetY, - keyShadowInfo.color + Color.argb(keyShadowInfo.alpha, 0f, 0f, 0f) ) onDrawCallback() canvas.restore() diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt new file mode 100644 index 000000000000..25d272185bc0 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.shared.shadow + +import android.content.Context +import android.graphics.Canvas +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.widget.TextView +import com.android.systemui.shared.R +import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo +import com.android.systemui.shared.shadow.DoubleShadowTextHelper.applyShadows + +/** Extension of [TextView] which draws two shadows on the text (ambient and key shadows} */ +class DoubleShadowTextView +@JvmOverloads +constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + defStyleRes: Int = 0 +) : TextView(context, attrs, defStyleAttr, defStyleRes) { + private val mKeyShadowInfo: ShadowInfo + private val mAmbientShadowInfo: ShadowInfo + + init { + val attributes = + context.obtainStyledAttributes( + attrs, + R.styleable.DoubleShadowTextView, + defStyleAttr, + defStyleRes + ) + val drawableSize: Int + val drawableInsetSize: Int + try { + val keyShadowBlur = + attributes.getDimensionPixelSize(R.styleable.DoubleShadowTextView_keyShadowBlur, 0) + val keyShadowOffsetX = + attributes.getDimensionPixelSize( + R.styleable.DoubleShadowTextView_keyShadowOffsetX, + 0 + ) + val keyShadowOffsetY = + attributes.getDimensionPixelSize( + R.styleable.DoubleShadowTextView_keyShadowOffsetY, + 0 + ) + val keyShadowAlpha = + attributes.getFloat(R.styleable.DoubleShadowTextView_keyShadowAlpha, 0f) + mKeyShadowInfo = + ShadowInfo( + keyShadowBlur.toFloat(), + keyShadowOffsetX.toFloat(), + keyShadowOffsetY.toFloat(), + keyShadowAlpha + ) + val ambientShadowBlur = + attributes.getDimensionPixelSize( + R.styleable.DoubleShadowTextView_ambientShadowBlur, + 0 + ) + val ambientShadowOffsetX = + attributes.getDimensionPixelSize( + R.styleable.DoubleShadowTextView_ambientShadowOffsetX, + 0 + ) + val ambientShadowOffsetY = + attributes.getDimensionPixelSize( + R.styleable.DoubleShadowTextView_ambientShadowOffsetY, + 0 + ) + val ambientShadowAlpha = + attributes.getFloat(R.styleable.DoubleShadowTextView_ambientShadowAlpha, 0f) + mAmbientShadowInfo = + ShadowInfo( + ambientShadowBlur.toFloat(), + ambientShadowOffsetX.toFloat(), + ambientShadowOffsetY.toFloat(), + ambientShadowAlpha + ) + drawableSize = + attributes.getDimensionPixelSize( + R.styleable.DoubleShadowTextView_drawableIconSize, + 0 + ) + drawableInsetSize = + attributes.getDimensionPixelSize( + R.styleable.DoubleShadowTextView_drawableIconInsetSize, + 0 + ) + } finally { + attributes.recycle() + } + + val drawables = arrayOf<Drawable?>(null, null, null, null) + for ((index, drawable) in compoundDrawablesRelative.withIndex()) { + if (drawable == null) continue + drawables[index] = + DoubleShadowIconDrawable( + mKeyShadowInfo, + mAmbientShadowInfo, + drawable, + drawableSize, + drawableInsetSize + ) + } + setCompoundDrawablesRelative(drawables[0], drawables[1], drawables[2], drawables[3]) + } + + public override fun onDraw(canvas: Canvas) { + applyShadows(mKeyShadowInfo, mAmbientShadowInfo, this, canvas) { super.onDraw(canvas) } + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java index f0210fd4c914..5d6598d63a1b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java @@ -49,6 +49,8 @@ public final class InteractionJankMonitorWrapper { InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET; public static final int CUJ_SPLIT_SCREEN_ENTER = InteractionJankMonitor.CUJ_SPLIT_SCREEN_ENTER; + public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION = + InteractionJankMonitor.CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION; @IntDef({ CUJ_APP_LAUNCH_FROM_RECENTS, @@ -57,6 +59,7 @@ public final class InteractionJankMonitorWrapper { CUJ_APP_CLOSE_TO_PIP, CUJ_QUICK_SWITCH, CUJ_APP_LAUNCH_FROM_WIDGET, + CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 97e024238778..85278dd4b883 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -47,6 +47,8 @@ public class QuickStepContract { public static final String KEY_EXTRA_SHELL_PIP = "extra_shell_pip"; // See ISplitScreen.aidl public static final String KEY_EXTRA_SHELL_SPLIT_SCREEN = "extra_shell_split_screen"; + // See IFloatingTasks.aidl + public static final String KEY_EXTRA_SHELL_FLOATING_TASKS = "extra_shell_floating_tasks"; // See IOneHanded.aidl public static final String KEY_EXTRA_SHELL_ONE_HANDED = "extra_shell_one_handed"; // See IShellTransitions.aidl @@ -60,6 +62,8 @@ public class QuickStepContract { // See IRecentTasks.aidl public static final String KEY_EXTRA_RECENT_TASKS = "recent_tasks"; public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation"; + // See IDesktopMode.aidl + public static final String KEY_EXTRA_SHELL_DESKTOP_MODE = "extra_shell_desktop_mode"; public static final String NAV_BAR_MODE_3BUTTON_OVERLAY = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt index f82e7dbd5d12..71470e8870de 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt @@ -52,17 +52,15 @@ data class KeyguardFaceListenModel( val becauseCannotSkipBouncer: Boolean, val biometricSettingEnabledForUser: Boolean, val bouncerFullyShown: Boolean, - val bouncerIsOrWillShow: Boolean, val faceAuthenticated: Boolean, val faceDisabled: Boolean, val faceLockedOut: Boolean, val fpLockedOut: Boolean, val goingToSleep: Boolean, - val keyguardAwakeExcludingBouncerShowing: Boolean, + val keyguardAwake: Boolean, val keyguardGoingAway: Boolean, val listeningForFaceAssistant: Boolean, val occludingAppRequestingFaceAuth: Boolean, - val onlyFaceEnrolled: Boolean, val primaryUser: Boolean, val scanningAllowedByStrongAuth: Boolean, val secureCameraLaunched: Boolean, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 2cc5ccdc3fa1..1e5c53de4446 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -24,6 +24,7 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; import android.animation.Animator; @@ -106,6 +107,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { return R.string.kg_prompt_reason_timeout_password; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: return R.string.kg_prompt_reason_timeout_password; + case PROMPT_REASON_TRUSTAGENT_EXPIRED: + return R.string.kg_prompt_reason_timeout_password; case PROMPT_REASON_NONE: return 0; default: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java index 987164557a7a..5b223242670c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -330,6 +330,9 @@ public class KeyguardPatternViewController case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern); break; + case PROMPT_REASON_TRUSTAGENT_EXPIRED: + mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern); + break; case PROMPT_REASON_NONE: break; default: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index c46e33d9fd53..0a91150e6c39 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -22,6 +22,7 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; import android.animation.Animator; @@ -123,6 +124,8 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView return R.string.kg_prompt_reason_timeout_pin; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: return R.string.kg_prompt_reason_timeout_pin; + case PROMPT_REASON_TRUSTAGENT_EXPIRED: + return R.string.kg_prompt_reason_timeout_pin; case PROMPT_REASON_NONE: return 0; default: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index f73c98e4971b..2bdb1b894c4a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -22,8 +22,6 @@ import static android.view.WindowInsets.Type.systemBars; import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY; -import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA; -import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA; import static java.lang.Integer.max; @@ -87,8 +85,8 @@ import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.SysUiStatsLog; +import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter; import com.android.systemui.statusbar.policy.UserSwitcherController; -import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter; import com.android.systemui.user.data.source.UserRecord; import com.android.systemui.util.settings.GlobalSettings; @@ -1098,6 +1096,7 @@ public class KeyguardSecurityContainer extends FrameLayout { return; } + mView.setAlpha(1f); mUserSwitcherViewGroup.setAlpha(0f); ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA, 1f); @@ -1137,7 +1136,7 @@ public class KeyguardSecurityContainer extends FrameLayout { KeyguardUserSwitcherAnchor anchor = mView.findViewById(R.id.user_switcher_anchor); - BaseUserAdapter adapter = new BaseUserAdapter(mUserSwitcherController) { + BaseUserSwitcherAdapter adapter = new BaseUserSwitcherAdapter(mUserSwitcherController) { @Override public View getView(int position, View convertView, ViewGroup parent) { UserRecord item = getItem(position); @@ -1172,8 +1171,7 @@ public class KeyguardSecurityContainer extends FrameLayout { } textView.setSelected(item == currentUser); view.setEnabled(item.isSwitchToEnabled); - view.setAlpha(view.isEnabled() ? USER_SWITCH_ENABLED_ALPHA : - USER_SWITCH_DISABLED_ALPHA); + UserSwitcherController.setSelectableAlpha(view); return view; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java index ac00e9453c97..9d0a8acf02b4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java @@ -61,6 +61,12 @@ public interface KeyguardSecurityView { int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7; /** + * Some auth is required because the trustagent expired either from timeout or manually by + * the user + */ + int PROMPT_REASON_TRUSTAGENT_EXPIRED = 8; + + /** * Reset the view and prepare to take input. This should do things like clearing the * password or pattern and clear error messages. */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java index d8cffd7984ba..5995e859c786 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java @@ -107,6 +107,14 @@ public class KeyguardSimPukViewController } @Override + public void onResume(int reason) { + super.onResume(reason); + if (mShowDefaultMessage) { + showDefaultMessage(); + } + } + + @Override void resetState() { super.resetState(); mStateMachine.reset(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 32c1cf9802ab..6eef3b33cf8f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -2593,11 +2593,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } final boolean statusBarShadeLocked = mStatusBarState == StatusBarState.SHADE_LOCKED; - // mKeyguardIsVisible is true even when the bouncer is shown, we don't want to run face auth - // on bouncer if both fp and fingerprint are enrolled. - final boolean awakeKeyguardExcludingBouncerShowing = mKeyguardIsVisible - && mDeviceInteractive && !mGoingToSleep - && !statusBarShadeLocked && !mBouncerIsOrWillBeShowing; + final boolean awakeKeyguard = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep + && !statusBarShadeLocked; final int user = getCurrentUser(); final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user); final boolean isLockDown = @@ -2637,16 +2634,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean faceDisabledForUser = isFaceDisabled(user); final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user); final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant(); - final boolean onlyFaceEnrolled = isOnlyFaceEnrolled(); final boolean fpOrFaceIsLockedOut = isFaceLockedOut() || fpLockedout; // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware. final boolean shouldListen = - ((mBouncerFullyShown && !mGoingToSleep && onlyFaceEnrolled) + (mBouncerFullyShown && !mGoingToSleep || mAuthInterruptActive || mOccludingAppRequestingFace - || awakeKeyguardExcludingBouncerShowing + || awakeKeyguard || shouldListenForFaceAssistant || mAuthController.isUdfpsFingerDown() || mUdfpsBouncerShowing) @@ -2667,17 +2663,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab becauseCannotSkipBouncer, biometricEnabledForUser, mBouncerFullyShown, - mBouncerIsOrWillBeShowing, faceAuthenticated, faceDisabledForUser, isFaceLockedOut(), fpLockedout, mGoingToSleep, - awakeKeyguardExcludingBouncerShowing, + awakeKeyguard, mKeyguardGoingAway, shouldListenForFaceAssistant, mOccludingAppRequestingFace, - onlyFaceEnrolled, mIsPrimaryUser, strongAuthAllowsScanning, mSecureCameraLaunched, @@ -2687,11 +2681,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return shouldListen; } - private boolean isOnlyFaceEnrolled() { - return isFaceEnrolled() - && !getCachedIsUnlockWithFingerprintPossible(sCurrentUser); - } - private void maybeLogListenerModelData(KeyguardListenModel model) { mLogger.logKeyguardListenerModel(model); @@ -3243,8 +3232,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab cb.onKeyguardBouncerStateChanged(mBouncerIsOrWillBeShowing); } } - updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, - FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN); + updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } if (wasBouncerFullyShown != mBouncerFullyShown) { diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 7fc81231c90b..a5fdc68226e8 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -103,7 +103,6 @@ import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; -import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -253,7 +252,6 @@ public class Dependency { @Inject Lazy<UserInfoController> mUserInfoController; @Inject Lazy<KeyguardStateController> mKeyguardMonitor; @Inject Lazy<KeyguardUpdateMonitor> mKeyguardUpdateMonitor; - @Inject Lazy<BatteryController> mBatteryController; @Inject Lazy<NightDisplayListener> mNightDisplayListener; @Inject Lazy<ReduceBrightColorsController> mReduceBrightColorsController; @Inject Lazy<ManagedProfileController> mManagedProfileController; @@ -404,8 +402,6 @@ public class Dependency { mProviders.put(UserInfoController.class, mUserInfoController::get); - mProviders.put(BatteryController.class, mBatteryController::get); - mProviders.put(NightDisplayListener.class, mNightDisplayListener::get); mProviders.put(ReduceBrightColorsController.class, mReduceBrightColorsController::get); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java index 5f586c927ef7..a21f45f701b3 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java @@ -96,7 +96,9 @@ public abstract class SystemUIInitializer { .setStartingSurface(mWMComponent.getStartingSurface()) .setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper()) .setRecentTasks(mWMComponent.getRecentTasks()) - .setBackAnimation(mWMComponent.getBackAnimation()); + .setBackAnimation(mWMComponent.getBackAnimation()) + .setFloatingTasks(mWMComponent.getFloatingTasks()) + .setDesktopMode(mWMComponent.getDesktopMode()); // Only initialize when not starting from tests since this currently initializes some // components that shouldn't be run in the test environment @@ -115,7 +117,9 @@ public abstract class SystemUIInitializer { .setDisplayAreaHelper(Optional.ofNullable(null)) .setStartingSurface(Optional.ofNullable(null)) .setRecentTasks(Optional.ofNullable(null)) - .setBackAnimation(Optional.ofNullable(null)); + .setBackAnimation(Optional.ofNullable(null)) + .setFloatingTasks(Optional.ofNullable(null)) + .setDesktopMode(Optional.ofNullable(null)); } mSysUIComponent = builder.build(); if (initializeComponents) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 8e4ba5ffa5fc..aae92adc5880 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -350,18 +350,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba @Override public void onTryAgainPressed(long requestId) { - if (mReceiver == null) { - Log.e(TAG, "onTryAgainPressed: Receiver is null"); - return; - } - - if (requestId != mCurrentDialog.getRequestId()) { - Log.w(TAG, "requestId doesn't match, skip onTryAgainPressed"); + final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId); + if (receiver == null) { + Log.w(TAG, "Skip onTryAgainPressed"); return; } try { - mReceiver.onTryAgainPressed(); + receiver.onTryAgainPressed(); } catch (RemoteException e) { Log.e(TAG, "RemoteException when handling try again", e); } @@ -369,18 +365,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba @Override public void onDeviceCredentialPressed(long requestId) { - if (mReceiver == null) { - Log.e(TAG, "onDeviceCredentialPressed: Receiver is null"); - return; - } - - if (requestId != mCurrentDialog.getRequestId()) { - Log.w(TAG, "requestId doesn't match, skip onDeviceCredentialPressed"); + final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId); + if (receiver == null) { + Log.w(TAG, "Skip onDeviceCredentialPressed"); return; } try { - mReceiver.onDeviceCredentialPressed(); + receiver.onDeviceCredentialPressed(); } catch (RemoteException e) { Log.e(TAG, "RemoteException when handling credential button", e); } @@ -388,18 +380,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba @Override public void onSystemEvent(int event, long requestId) { - if (mReceiver == null) { - Log.e(TAG, "onSystemEvent(" + event + "): Receiver is null"); - return; - } - - if (requestId != mCurrentDialog.getRequestId()) { - Log.w(TAG, "requestId doesn't match, skip onSystemEvent"); + final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId); + if (receiver == null) { + Log.w(TAG, "Skip onSystemEvent"); return; } try { - mReceiver.onSystemEvent(event); + receiver.onSystemEvent(event); } catch (RemoteException e) { Log.e(TAG, "RemoteException when sending system event", e); } @@ -407,23 +395,46 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba @Override public void onDialogAnimatedIn(long requestId) { - if (mReceiver == null) { - Log.e(TAG, "onDialogAnimatedIn: Receiver is null"); - return; - } - - if (requestId != mCurrentDialog.getRequestId()) { - Log.w(TAG, "requestId doesn't match, skip onDialogAnimatedIn"); + final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId); + if (receiver == null) { + Log.w(TAG, "Skip onDialogAnimatedIn"); return; } try { - mReceiver.onDialogAnimatedIn(); + receiver.onDialogAnimatedIn(); } catch (RemoteException e) { Log.e(TAG, "RemoteException when sending onDialogAnimatedIn", e); } } + @Nullable + private IBiometricSysuiReceiver getCurrentReceiver(long requestId) { + if (!isRequestIdValid(requestId)) { + return null; + } + + if (mReceiver == null) { + Log.w(TAG, "getCurrentReceiver: Receiver is null"); + } + + return mReceiver; + } + + private boolean isRequestIdValid(long requestId) { + if (mCurrentDialog == null) { + Log.w(TAG, "shouldNotifyReceiver: dialog already gone"); + return false; + } + + if (requestId != mCurrentDialog.getRequestId()) { + Log.w(TAG, "shouldNotifyReceiver: requestId doesn't match"); + return false; + } + + return true; + } + @Override public void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation, long requestId) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 27e9af9b394a..412dc0577876 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -912,6 +912,12 @@ public class UdfpsController implements DozeReceiver { if (view.isDisplayConfigured()) { view.unconfigureDisplay(); } + + if (mCancelAodTimeoutAction != null) { + mCancelAodTimeoutAction.run(); + mCancelAodTimeoutAction = null; + } + mIsAodInterruptActive = false; } /** diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java index bfbf37a5d3e9..d53e56f7b852 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java @@ -220,7 +220,7 @@ public class BrightLineFalsingManager implements FalsingManager { return r; }).collect(Collectors.toList()); - logDebug("False Gesture: " + localResult[0]); + logDebug("False Gesture (type: " + interactionType + "): " + localResult[0]); return localResult[0]; } @@ -454,6 +454,12 @@ public class BrightLineFalsingManager implements FalsingManager { } } + static void logVerbose(String msg) { + if (DEBUG) { + Log.v(TAG, msg); + } + } + static void logInfo(String msg) { Log.i(TAG, msg); RECENT_INFO_LOG.add(msg); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java index f18413be0134..c2922968b58e 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java @@ -42,8 +42,9 @@ public abstract class Classifier { public static final int QS_COLLAPSE = 12; public static final int UDFPS_AUTHENTICATION = 13; public static final int LOCK_ICON = 14; - public static final int QS_SWIPE = 15; + public static final int QS_SWIPE_SIDE = 15; public static final int BACK_GESTURE = 16; + public static final int QS_SWIPE_NESTED = 17; @IntDef({ QUICK_SETTINGS, @@ -62,7 +63,8 @@ public abstract class Classifier { BRIGHTNESS_SLIDER, UDFPS_AUTHENTICATION, LOCK_ICON, - QS_SWIPE, + QS_SWIPE_SIDE, + QS_SWIPE_NESTED, BACK_GESTURE }) @Retention(RetentionPolicy.SOURCE) diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java index d0fe1c37d4fa..5e4f149d3ca3 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java @@ -24,6 +24,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHT import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN; import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER; import static com.android.systemui.classifier.Classifier.QS_COLLAPSE; +import static com.android.systemui.classifier.Classifier.QS_SWIPE_NESTED; import static com.android.systemui.classifier.Classifier.SHADE_DRAG; import android.provider.DeviceConfig; @@ -156,7 +157,8 @@ class DistanceClassifier extends FalsingClassifier { || interactionType == QS_COLLAPSE || interactionType == Classifier.UDFPS_AUTHENTICATION || interactionType == Classifier.LOCK_ICON - || interactionType == Classifier.QS_SWIPE) { + || interactionType == Classifier.QS_SWIPE_SIDE + || interactionType == QS_SWIPE_NESTED) { return Result.passed(0); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java index d75752841023..d18d62f025fe 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java @@ -148,6 +148,11 @@ public abstract class FalsingClassifier { } /** */ + public static void logVerbose(String msg) { + BrightLineFalsingManager.logVerbose(msg); + } + + /** */ public static void logInfo(String msg) { BrightLineFalsingManager.logInfo(msg); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java index a3ecb0c0b273..3991a35e958a 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java @@ -78,10 +78,10 @@ public class FalsingDataProvider { void onMotionEvent(MotionEvent motionEvent) { List<MotionEvent> motionEvents = unpackMotionEvent(motionEvent); - FalsingClassifier.logDebug("Unpacked into: " + motionEvents.size()); + FalsingClassifier.logVerbose("Unpacked into: " + motionEvents.size()); if (BrightLineFalsingManager.DEBUG) { for (MotionEvent m : motionEvents) { - FalsingClassifier.logDebug( + FalsingClassifier.logVerbose( "x,y,t: " + m.getX() + "," + m.getY() + "," + m.getEventTime()); } } @@ -92,7 +92,7 @@ public class FalsingDataProvider { } mRecentMotionEvents.addAll(motionEvents); - FalsingClassifier.logDebug("Size: " + mRecentMotionEvents.size()); + FalsingClassifier.logVerbose("Size: " + mRecentMotionEvents.size()); mMotionEventListeners.forEach(listener -> listener.onMotionEvent(motionEvent)); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java index 32d9ca59951d..07f94e792a93 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java @@ -19,7 +19,7 @@ package com.android.systemui.classifier; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD; import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER; import static com.android.systemui.classifier.Classifier.QS_COLLAPSE; -import static com.android.systemui.classifier.Classifier.QS_SWIPE; +import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import android.provider.DeviceConfig; @@ -119,7 +119,7 @@ class ProximityClassifier extends FalsingClassifier { @Classifier.InteractionType int interactionType, double historyBelief, double historyConfidence) { if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER - || interactionType == QS_COLLAPSE || interactionType == QS_SWIPE) { + || interactionType == QS_COLLAPSE || interactionType == QS_SWIPE_SIDE) { return Result.passed(0); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java index f040712706cd..776bc88ad6bf 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java @@ -24,7 +24,8 @@ import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS; import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN; import static com.android.systemui.classifier.Classifier.PULSE_EXPAND; import static com.android.systemui.classifier.Classifier.QS_COLLAPSE; -import static com.android.systemui.classifier.Classifier.QS_SWIPE; +import static com.android.systemui.classifier.Classifier.QS_SWIPE_NESTED; +import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; import static com.android.systemui.classifier.Classifier.SHADE_DRAG; @@ -86,9 +87,12 @@ public class TypeClassifier extends FalsingClassifier { case QS_COLLAPSE: wrongDirection = !vertical || !up; break; - case QS_SWIPE: + case QS_SWIPE_SIDE: wrongDirection = vertical; break; + case QS_SWIPE_NESTED: + wrongDirection = !vertical; + break; default: wrongDirection = true; break; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java index 40c28fab51b8..de2bdf7ded75 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java @@ -137,8 +137,8 @@ class ZigZagClassifier extends FalsingClassifier { runningAbsDy += Math.abs(point.y - pY); pX = point.x; pY = point.y; - logDebug("(x, y, runningAbsDx, runningAbsDy) - (" + pX + ", " + pY + ", " + runningAbsDx - + ", " + runningAbsDy + ")"); + logVerbose("(x, y, runningAbsDx, runningAbsDy) - (" + + pX + ", " + pY + ", " + runningAbsDx + ", " + runningAbsDy + ")"); } float devianceX = runningAbsDx - actualDx; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index fbfc94af683e..a99669970a34 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -35,6 +35,7 @@ import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.doze.DozeHost; +import com.android.systemui.dump.DumpManager; import com.android.systemui.media.dagger.MediaModule; import com.android.systemui.navigationbar.gestural.GestureModule; import com.android.systemui.plugins.qs.QSFactory; @@ -126,6 +127,7 @@ public abstract class ReferenceSystemUIModule { PowerManager powerManager, BroadcastDispatcher broadcastDispatcher, DemoModeController demoModeController, + DumpManager dumpManager, @Main Handler mainHandler, @Background Handler bgHandler) { BatteryController bC = new BatteryControllerImpl( @@ -134,6 +136,7 @@ public abstract class ReferenceSystemUIModule { powerManager, broadcastDispatcher, demoModeController, + dumpManager, mainHandler, bgHandler); bC.init(); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 029cabb0bc0b..0d06c513d248 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -40,7 +40,9 @@ import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider; import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.bubbles.Bubbles; +import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; +import com.android.wm.shell.floating.FloatingTasks; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.recents.RecentTasks; @@ -109,6 +111,12 @@ public interface SysUIComponent { @BindsInstance Builder setBackAnimation(Optional<BackAnimation> b); + @BindsInstance + Builder setFloatingTasks(Optional<FloatingTasks> f); + + @BindsInstance + Builder setDesktopMode(Optional<DesktopMode> d); + SysUIComponent build(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 0469152de776..443d2774f0e0 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -41,6 +41,7 @@ import com.android.systemui.dreams.dagger.DreamModule; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FlagsModule; import com.android.systemui.fragments.FragmentService; +import com.android.systemui.keyguard.data.BouncerViewModule; import com.android.systemui.log.dagger.LogModule; import com.android.systemui.media.dagger.MediaProjectionModule; import com.android.systemui.model.SysUiState; @@ -116,6 +117,7 @@ import dagger.Provides; AppOpsModule.class, AssistModule.class, BiometricsModule.class, + BouncerViewModule.class, ClockModule.class, CoroutinesModule.class, DreamModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index b6923a867507..096f96949382 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -23,8 +23,6 @@ import androidx.annotation.Nullable; import com.android.systemui.SystemUIInitializerFactory; import com.android.systemui.tv.TvWMComponent; -import com.android.wm.shell.sysui.ShellCommandHandler; -import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.bubbles.Bubbles; @@ -32,7 +30,9 @@ import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.dagger.TvWMShellModule; import com.android.wm.shell.dagger.WMShellModule; import com.android.wm.shell.dagger.WMSingleton; +import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; +import com.android.wm.shell.floating.FloatingTasks; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.recents.RecentTasks; @@ -110,4 +110,13 @@ public interface WMComponent { @WMSingleton Optional<BackAnimation> getBackAnimation(); + + @WMSingleton + Optional<FloatingTasks> getFloatingTasks(); + + /** + * Optional {@link DesktopMode} component for interacting with desktop mode. + */ + @WMSingleton + Optional<DesktopMode> getDesktopMode(); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java index d7b7777559da..733a80dd7f69 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -35,6 +35,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.complication.ComplicationHostViewController; import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.dreams.dagger.DreamOverlayModule; +import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -73,6 +74,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve // Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates). private final Handler mHandler; private final int mDreamOverlayMaxTranslationY; + private final BouncerCallbackInteractor mBouncerCallbackInteractor; private long mJitterStartTimeMillis; @@ -131,7 +133,8 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve @Named(DreamOverlayModule.MAX_BURN_IN_OFFSET) int maxBurnInOffset, @Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long burnInProtectionUpdateInterval, - @Named(DreamOverlayModule.MILLIS_UNTIL_FULL_JITTER) long millisUntilFullJitter) { + @Named(DreamOverlayModule.MILLIS_UNTIL_FULL_JITTER) long millisUntilFullJitter, + BouncerCallbackInteractor bouncerCallbackInteractor) { super(containerView); mDreamOverlayContentView = contentView; mStatusBarViewController = statusBarViewController; @@ -151,6 +154,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve mMaxBurnInOffset = maxBurnInOffset; mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval; mMillisUntilFullJitter = millisUntilFullJitter; + mBouncerCallbackInteractor = bouncerCallbackInteractor; } @Override @@ -167,6 +171,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve if (bouncer != null) { bouncer.addBouncerExpansionCallback(mBouncerExpansionCallback); } + mBouncerCallbackInteractor.addBouncerExpansionCallback(mBouncerExpansionCallback); } @Override @@ -176,6 +181,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve if (bouncer != null) { bouncer.removeBouncerExpansionCallback(mBouncerExpansionCallback); } + mBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback); } View getContainerView() { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index 96f77b3654c5..696fc7254308 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -16,6 +16,7 @@ package com.android.systemui.dreams; +import android.content.ComponentName; import android.content.Context; import android.graphics.drawable.ColorDrawable; import android.util.Log; @@ -26,11 +27,13 @@ import android.view.WindowInsets; import android.view.WindowManager; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleRegistry; import androidx.lifecycle.ViewModelStore; +import com.android.dream.lowlight.dagger.LowLightDreamModule; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.policy.PhoneWindow; @@ -44,6 +47,7 @@ import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor; import java.util.concurrent.Executor; import javax.inject.Inject; +import javax.inject.Named; /** * The {@link DreamOverlayService} is responsible for placing an overlay on top of a dream. The @@ -62,6 +66,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ // content area). private final DreamOverlayContainerViewController mDreamOverlayContainerViewController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Nullable + private final ComponentName mLowLightDreamComponent; private final UiEventLogger mUiEventLogger; // A reference to the {@link Window} used to hold the dream overlay. @@ -125,10 +131,13 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ DreamOverlayComponent.Factory dreamOverlayComponentFactory, DreamOverlayStateController stateController, KeyguardUpdateMonitor keyguardUpdateMonitor, - UiEventLogger uiEventLogger) { + UiEventLogger uiEventLogger, + @Nullable @Named(LowLightDreamModule.LOW_LIGHT_DREAM_COMPONENT) + ComponentName lowLightDreamComponent) { mContext = context; mExecutor = executor; mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mLowLightDreamComponent = lowLightDreamComponent; mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback); mStateController = stateController; mUiEventLogger = uiEventLogger; @@ -155,6 +164,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ windowManager.removeView(mWindow.getDecorView()); } mStateController.setOverlayActive(false); + mStateController.setLowLightActive(false); mDestroyed = true; super.onDestroy(); } @@ -163,6 +173,9 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) { mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_ENTER_START); setCurrentState(Lifecycle.State.STARTED); + final ComponentName dreamComponent = getDreamComponent(); + mStateController.setLowLightActive( + dreamComponent != null && dreamComponent.equals(mLowLightDreamComponent)); mExecutor.execute(() -> { if (mDestroyed) { // The task could still be executed after the service has been destroyed. Bail if diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java index 69e41ba9b284..72feaca59ace 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java @@ -50,6 +50,7 @@ public class DreamOverlayStateController implements private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); public static final int STATE_DREAM_OVERLAY_ACTIVE = 1 << 0; + public static final int STATE_LOW_LIGHT_ACTIVE = 1 << 1; private static final int OP_CLEAR_STATE = 1; private static final int OP_SET_STATE = 2; @@ -193,6 +194,14 @@ public class DreamOverlayStateController implements return containsState(STATE_DREAM_OVERLAY_ACTIVE); } + /** + * Returns whether low light mode is active. + * @return {@code true} if in low light mode, {@code false} otherwise. + */ + public boolean isLowLightActive() { + return containsState(STATE_LOW_LIGHT_ACTIVE); + } + private boolean containsState(int state) { return (mState & state) != 0; } @@ -222,6 +231,14 @@ public class DreamOverlayStateController implements } /** + * Sets whether low light mode is active. + * @param active {@code true} if low light mode is active, {@code false} otherwise. + */ + public void setLowLightActive(boolean active) { + modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_LOW_LIGHT_ACTIVE); + } + + /** * Returns the available complication types. */ @Complication.ComplicationType diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java index aa59cc666caa..bb1c4303041a 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java @@ -20,7 +20,6 @@ import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; import static android.app.StatusBarManager.WINDOW_STATE_HIDING; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; -import android.annotation.Nullable; import android.app.AlarmManager; import android.app.StatusBarManager; import android.content.res.Resources; @@ -36,6 +35,8 @@ import android.text.format.DateFormat; import android.util.PluralsMessageFormatter; import android.view.View; +import androidx.annotation.Nullable; + import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem; @@ -73,6 +74,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve private final Optional<DreamOverlayNotificationCountProvider> mDreamOverlayNotificationCountProvider; private final ZenModeController mZenModeController; + private final DreamOverlayStateController mDreamOverlayStateController; + private final StatusBarWindowStateController mStatusBarWindowStateController; private final DreamOverlayStatusBarItemsProvider mStatusBarItemsProvider; private final Executor mMainExecutor; private final List<DreamOverlayStatusBarItemsProvider.StatusBarItem> mExtraStatusBarItems = @@ -102,6 +105,14 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve } }; + private final DreamOverlayStateController.Callback mDreamOverlayStateCallback = + new DreamOverlayStateController.Callback() { + @Override + public void onStateChanged() { + updateLowLightState(); + } + }; + private final IndividualSensorPrivacyController.Callback mSensorCallback = (sensor, blocked) -> updateMicCameraBlockedStatusIcon(); @@ -140,7 +151,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve Optional<DreamOverlayNotificationCountProvider> dreamOverlayNotificationCountProvider, ZenModeController zenModeController, StatusBarWindowStateController statusBarWindowStateController, - DreamOverlayStatusBarItemsProvider statusBarItemsProvider) { + DreamOverlayStatusBarItemsProvider statusBarItemsProvider, + DreamOverlayStateController dreamOverlayStateController) { super(view); mResources = resources; mMainExecutor = mainExecutor; @@ -151,8 +163,10 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve mDateFormatUtil = dateFormatUtil; mSensorPrivacyController = sensorPrivacyController; mDreamOverlayNotificationCountProvider = dreamOverlayNotificationCountProvider; + mStatusBarWindowStateController = statusBarWindowStateController; mStatusBarItemsProvider = statusBarItemsProvider; mZenModeController = zenModeController; + mDreamOverlayStateController = dreamOverlayStateController; // Register to receive show/hide updates for the system status bar. Our custom status bar // needs to hide when the system status bar is showing to ovoid overlapping status bars. @@ -180,6 +194,9 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve mStatusBarItemsProvider.addCallback(mStatusBarItemsProviderCallback); + mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback); + updateLowLightState(); + mTouchInsetSession.addViewToTracking(mView); } @@ -193,6 +210,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve provider -> provider.removeCallback(mNotificationCountCallback)); mStatusBarItemsProvider.removeCallback(mStatusBarItemsProviderCallback); mView.removeAllExtraStatusBarItemViews(); + mDreamOverlayStateController.removeCallback(mDreamOverlayStateCallback); mTouchInsetSession.clear(); mIsAttached = false; @@ -217,6 +235,15 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve hasAlarm ? buildAlarmContentDescription(alarm) : null); } + private void updateLowLightState() { + int visibility = View.VISIBLE; + if (mDreamOverlayStateController.isLowLightActive() + || mStatusBarWindowStateController.windowIsShowing()) { + visibility = View.INVISIBLE; + } + mView.setVisibility(visibility); + } + private String buildAlarmContentDescription(AlarmManager.AlarmClockInfo alarm) { final String skeleton = mDateFormatUtil.is24HourFormat() ? "EHm" : "Ehma"; final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); @@ -272,7 +299,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve private void onSystemStatusBarStateChanged(@StatusBarManager.WindowVisibleState int state) { mMainExecutor.execute(() -> { - if (!mIsAttached) { + if (!mIsAttached || mDreamOverlayStateController.isLowLightActive()) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java deleted file mode 100644 index 789ebc517271..000000000000 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.dreams.complication; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.util.AttributeSet; -import android.widget.TextClock; - -import com.android.systemui.R; -import com.android.systemui.dreams.complication.DoubleShadowTextHelper.ShadowInfo; - -import kotlin.Unit; - -/** - * Extension of {@link TextClock} which draws two shadows on the text (ambient and key shadows) - */ -public class DoubleShadowTextClock extends TextClock { - private final DoubleShadowTextHelper mShadowHelper; - - public DoubleShadowTextClock(Context context) { - this(context, null); - } - - public DoubleShadowTextClock(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public DoubleShadowTextClock(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - final Resources resources = context.getResources(); - final ShadowInfo keyShadowInfo = new ShadowInfo( - resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_radius), - resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_dx), - resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_dy), - resources.getColor(R.color.dream_overlay_clock_key_text_shadow_color)); - - final ShadowInfo ambientShadowInfo = new ShadowInfo( - resources.getDimensionPixelSize( - R.dimen.dream_overlay_clock_ambient_text_shadow_radius), - resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_ambient_text_shadow_dx), - resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_ambient_text_shadow_dy), - resources.getColor(R.color.dream_overlay_clock_ambient_text_shadow_color)); - mShadowHelper = new DoubleShadowTextHelper(keyShadowInfo, ambientShadowInfo); - } - - @Override - public void onDraw(Canvas canvas) { - mShadowHelper.applyShadows(this, canvas, () -> { - super.onDraw(canvas); - return Unit.INSTANCE; - }); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextView.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextView.java deleted file mode 100644 index cf7e3127dedf..000000000000 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextView.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.dreams.complication; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.util.AttributeSet; -import android.widget.TextView; - -import com.android.systemui.R; - -import kotlin.Unit; - -/** - * Extension of {@link TextView} which draws two shadows on the text (ambient and key shadows} - */ -public class DoubleShadowTextView extends TextView { - private final DoubleShadowTextHelper mShadowHelper; - - public DoubleShadowTextView(Context context) { - this(context, null); - } - - public DoubleShadowTextView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public DoubleShadowTextView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - final Resources resources = context.getResources(); - final DoubleShadowTextHelper.ShadowInfo - keyShadowInfo = new DoubleShadowTextHelper.ShadowInfo( - resources.getDimensionPixelSize( - R.dimen.dream_overlay_status_bar_key_text_shadow_radius), - resources.getDimensionPixelSize( - R.dimen.dream_overlay_status_bar_key_text_shadow_dx), - resources.getDimensionPixelSize( - R.dimen.dream_overlay_status_bar_key_text_shadow_dy), - resources.getColor(R.color.dream_overlay_status_bar_key_text_shadow_color)); - - final DoubleShadowTextHelper.ShadowInfo - ambientShadowInfo = new DoubleShadowTextHelper.ShadowInfo( - resources.getDimensionPixelSize( - R.dimen.dream_overlay_status_bar_ambient_text_shadow_radius), - resources.getDimensionPixelSize( - R.dimen.dream_overlay_status_bar_ambient_text_shadow_dx), - resources.getDimensionPixelSize( - R.dimen.dream_overlay_status_bar_ambient_text_shadow_dy), - resources.getColor(R.color.dream_overlay_status_bar_ambient_text_shadow_color)); - mShadowHelper = new DoubleShadowTextHelper(keyShadowInfo, ambientShadowInfo); - } - - @Override - public void onDraw(Canvas canvas) { - mShadowHelper.applyShadows(this, canvas, () -> { - super.onDraw(canvas); - return Unit.INSTANCE; - }); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java index 21a51d1096d5..c07d4022df76 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java @@ -18,13 +18,21 @@ package com.android.systemui.dreams.complication; import static com.android.systemui.dreams.complication.dagger.DreamMediaEntryComplicationComponent.DreamMediaEntryModule.DREAM_MEDIA_ENTRY_VIEW; import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_MEDIA_ENTRY_LAYOUT_PARAMS; +import static com.android.systemui.flags.Flags.DREAM_MEDIA_TAP_TO_OPEN; +import android.app.PendingIntent; import android.util.Log; import android.view.View; +import com.android.systemui.ActivityIntentHelper; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dreams.complication.dagger.DreamMediaEntryComplicationComponent; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.media.MediaCarouselController; import com.android.systemui.media.dream.MediaDreamComplication; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.ViewController; import javax.inject.Inject; @@ -87,6 +95,15 @@ public class DreamMediaEntryComplication implements Complication { private final DreamOverlayStateController mDreamOverlayStateController; private final MediaDreamComplication mMediaComplication; + private final MediaCarouselController mMediaCarouselController; + + private final ActivityStarter mActivityStarter; + private final ActivityIntentHelper mActivityIntentHelper; + private final KeyguardStateController mKeyguardStateController; + private final NotificationLockscreenUserManager mLockscreenUserManager; + + private final FeatureFlags mFeatureFlags; + private boolean mIsTapToOpenEnabled; private boolean mMediaComplicationAdded; @@ -94,15 +111,28 @@ public class DreamMediaEntryComplication implements Complication { DreamMediaEntryViewController( @Named(DREAM_MEDIA_ENTRY_VIEW) View view, DreamOverlayStateController dreamOverlayStateController, - MediaDreamComplication mediaComplication) { + MediaDreamComplication mediaComplication, + MediaCarouselController mediaCarouselController, + ActivityStarter activityStarter, + ActivityIntentHelper activityIntentHelper, + KeyguardStateController keyguardStateController, + NotificationLockscreenUserManager lockscreenUserManager, + FeatureFlags featureFlags) { super(view); mDreamOverlayStateController = dreamOverlayStateController; mMediaComplication = mediaComplication; + mMediaCarouselController = mediaCarouselController; + mActivityStarter = activityStarter; + mActivityIntentHelper = activityIntentHelper; + mKeyguardStateController = keyguardStateController; + mLockscreenUserManager = lockscreenUserManager; + mFeatureFlags = featureFlags; mView.setOnClickListener(this::onClickMediaEntry); } @Override protected void onViewAttached() { + mIsTapToOpenEnabled = mFeatureFlags.isEnabled(DREAM_MEDIA_TAP_TO_OPEN); } @Override @@ -113,6 +143,31 @@ public class DreamMediaEntryComplication implements Complication { private void onClickMediaEntry(View v) { if (DEBUG) Log.d(TAG, "media entry complication tapped"); + if (mIsTapToOpenEnabled) { + final PendingIntent clickIntent = + mMediaCarouselController.getCurrentVisibleMediaContentIntent(); + + if (clickIntent == null) { + return; + } + + // See StatusBarNotificationActivityStarter#onNotificationClicked + final boolean showOverLockscreen = mKeyguardStateController.isShowing() + && mActivityIntentHelper.wouldShowOverLockscreen(clickIntent.getIntent(), + mLockscreenUserManager.getCurrentUserId()); + + if (showOverLockscreen) { + mActivityStarter.startActivity(clickIntent.getIntent(), + /* dismissShade */ true, + /* animationController */ null, + /* showOverLockscreenWhenLocked */ true); + } else { + mActivityStarter.postStartActivityDismissingKeyguard(clickIntent, null); + } + + return; + } + if (!mMediaComplicationAdded) { addMediaComplication(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java index 2dd2098a78b9..f9dca08ae14f 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java @@ -19,6 +19,7 @@ package com.android.systemui.dreams.dagger; import android.content.Context; import android.content.res.Resources; +import com.android.dream.lowlight.dagger.LowLightDreamModule; import com.android.settingslib.dream.DreamBackend; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -37,6 +38,7 @@ import dagger.Provides; */ @Module(includes = { RegisteredComplicationsModule.class, + LowLightDreamModule.class, }, subcomponents = { DreamOverlayComponent.class, diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index cd6c57aea76b..93f13ebb3892 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -101,8 +101,12 @@ public class Flags { public static final UnreleasedFlag MODERN_BOUNCER = new UnreleasedFlag(208); /** Whether UserSwitcherActivity should use modern architecture. */ - public static final UnreleasedFlag MODERN_USER_SWITCHER_ACTIVITY = - new UnreleasedFlag(209, true); + public static final ReleasedFlag MODERN_USER_SWITCHER_ACTIVITY = + new ReleasedFlag(209, true); + + /** Whether the new implementation of UserSwitcherController should be used. */ + public static final UnreleasedFlag REFACTORED_USER_SWITCHER_CONTROLLER = + new UnreleasedFlag(210, false); /***************************************/ // 300 - power menu @@ -192,11 +196,12 @@ public class Flags { /***************************************/ // 900 - media - public static final ReleasedFlag MEDIA_TAP_TO_TRANSFER = new ReleasedFlag(900); + public static final UnreleasedFlag MEDIA_TAP_TO_TRANSFER = new UnreleasedFlag(900); public static final UnreleasedFlag MEDIA_SESSION_ACTIONS = new UnreleasedFlag(901); public static final ReleasedFlag MEDIA_NEARBY_DEVICES = new ReleasedFlag(903); public static final ReleasedFlag MEDIA_MUTE_AWAIT = new ReleasedFlag(904); - public static final UnreleasedFlag MEDIA_DREAM_COMPLICATION = new UnreleasedFlag(905); + public static final UnreleasedFlag DREAM_MEDIA_COMPLICATION = new UnreleasedFlag(905); + public static final UnreleasedFlag DREAM_MEDIA_TAP_TO_OPEN = new UnreleasedFlag(906); // 1000 - dock public static final ReleasedFlag SIMULATE_DOCK_THROUGH_CHARGING = @@ -234,6 +239,14 @@ public class Flags { public static final SysPropBooleanFlag WM_CAPTION_ON_SHELL = new SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false); + @Keep + public static final SysPropBooleanFlag FLOATING_TASKS_ENABLED = + new SysPropBooleanFlag(1106, "persist.wm.debug.floating_tasks", false); + + @Keep + public static final SysPropBooleanFlag SHOW_FLOATING_TASKS_AS_BUBBLES = + new SysPropBooleanFlag(1107, "persist.wm.debug.floating_tasks_as_bubbles", false); + // 1200 - predictive back @Keep public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index be1d162e2b08..38b98eb45aec 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -22,8 +22,10 @@ import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_UNLOCK_ANIMATION; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; @@ -802,6 +804,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } else if (trustAgentsEnabled && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) { return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; + } else if (trustAgentsEnabled + && (strongAuth & SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED) != 0) { + return KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED; } else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0 || mUpdateMonitor.isFingerprintLockedOut())) { return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT; @@ -841,6 +846,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, if (launchIsFullScreen) { mCentralSurfaces.instantCollapseNotificationPanel(); } + + mInteractionJankMonitor.end(CUJ_LOCKSCREEN_OCCLUSION); } @NonNull @@ -987,6 +994,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, setOccluded(isKeyguardOccluded /* isOccluded */, false /* animate */); Log.d(TAG, "Unocclude animation cancelled. Occluded state is now: " + mOccluded); + + mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_OCCLUSION); } @Override @@ -995,6 +1004,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { Log.d(TAG, "UnoccludeAnimator#onAnimationStart. Set occluded = false."); + mInteractionJankMonitor.begin( + createInteractionJankMonitorConf(CUJ_LOCKSCREEN_OCCLUSION) + .setTag("UNOCCLUDE")); setOccluded(false /* isOccluded */, true /* animate */); if (apps == null || apps.length == 0 || apps[0] == null) { @@ -1053,6 +1065,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, try { finishedCallback.onAnimationFinished(); mUnoccludeAnimator = null; + + mInteractionJankMonitor.end(CUJ_LOCKSCREEN_OCCLUSION); } catch (RemoteException e) { e.printStackTrace(); } @@ -2569,7 +2583,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, }; try { mInteractionJankMonitor.begin( - createInteractionJankMonitorConf("RunRemoteAnimation")); + createInteractionJankMonitorConf( + CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "RunRemoteAnimation")); runner.onAnimationStart(WindowManager.TRANSIT_KEYGUARD_GOING_AWAY, apps, wallpapers, nonApps, callback); } catch (RemoteException e) { @@ -2585,7 +2600,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mSurfaceBehindRemoteAnimationRunning = true; mInteractionJankMonitor.begin( - createInteractionJankMonitorConf("DismissPanel")); + createInteractionJankMonitorConf( + CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "DismissPanel")); // Pass the surface and metadata to the unlock animation controller. mKeyguardUnlockAnimationControllerLazy.get() @@ -2593,7 +2609,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, apps, startTime, mSurfaceBehindRemoteAnimationRequested); } else { mInteractionJankMonitor.begin( - createInteractionJankMonitorConf("RemoteAnimationDisabled")); + createInteractionJankMonitorConf( + CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "RemoteAnimationDisabled")); mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration); @@ -2673,10 +2690,15 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, sendUserPresentBroadcast(); } - private Configuration.Builder createInteractionJankMonitorConf(String tag) { - return Configuration.Builder.withView(CUJ_LOCKSCREEN_UNLOCK_ANIMATION, - mKeyguardViewControllerLazy.get().getViewRootImpl().getView()) - .setTag(tag); + private Configuration.Builder createInteractionJankMonitorConf(int cuj) { + return createInteractionJankMonitorConf(cuj, null /* tag */); + } + + private Configuration.Builder createInteractionJankMonitorConf(int cuj, @Nullable String tag) { + final Configuration.Builder builder = Configuration.Builder.withView( + cuj, mKeyguardViewControllerLazy.get().getViewRootImpl().getView()); + + return tag != null ? builder.setTag(tag) : builder; } /** @@ -3287,6 +3309,10 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { super.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback); + mInteractionJankMonitor.begin( + createInteractionJankMonitorConf(CUJ_LOCKSCREEN_OCCLUSION) + .setTag("OCCLUDE")); + // This is the first signal we have from WM that we're going to be occluded. Set our // internal state to reflect that immediately, vs. waiting for the launch animator to // begin. Otherwise, calls to setShowingLocked, etc. will not know that we're about to @@ -3303,6 +3329,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, + "Setting occluded state to: " + isKeyguardOccluded); setOccluded(isKeyguardOccluded /* occluded */, false /* animate */); + mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_OCCLUSION); } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt new file mode 100644 index 000000000000..99ae85d7a548 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.data + +import android.view.KeyEvent +import com.android.systemui.dagger.SysUISingleton +import java.lang.ref.WeakReference +import javax.inject.Inject + +/** An abstraction to interface with the ui layer, without changing state. */ +interface BouncerView { + var delegate: BouncerViewDelegate? +} + +/** A lightweight class to hold reference to the ui delegate. */ +@SysUISingleton +class BouncerViewImpl @Inject constructor() : BouncerView { + private var _delegate: WeakReference<BouncerViewDelegate?> = WeakReference(null) + override var delegate: BouncerViewDelegate? + get() = _delegate.get() + set(value) { + _delegate = WeakReference(value) + } +} + +/** An abstraction that implements view logic. */ +interface BouncerViewDelegate { + fun isFullScreenBouncer(): Boolean + fun shouldDismissOnMenuPressed(): Boolean + fun interceptMediaKey(event: KeyEvent?): Boolean + fun dispatchBackKeyEventPreIme(): Boolean + fun showNextSecurityScreenOrFinish(): Boolean + fun resume() +} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ExampleFeatureScreen.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt index 6e1721490f98..390c54e0350a 100644 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ExampleFeatureScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.android.systemui.compose.gallery +package com.android.systemui.keyguard.data -import androidx.compose.foundation.layout.Column -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.android.systemui.ExampleFeature +import dagger.Binds +import dagger.Module -/** The screen that shows ExampleFeature. */ -@Composable -fun ExampleFeatureScreen(modifier: Modifier = Modifier) { - Column(modifier) { ExampleFeature("This is an example feature!") } +@Module +interface BouncerViewModule { + /** Binds BouncerView to BouncerViewImpl and makes it injectable. */ + @Binds fun bindBouncerView(bouncerViewImpl: BouncerViewImpl): BouncerView } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt new file mode 100644 index 000000000000..543389e0a7cd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.data.repository + +import android.hardware.biometrics.BiometricSourceType +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.keyguard.KeyguardUpdateMonitorCallback +import com.android.keyguard.ViewMediatorCallback +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel +import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel +import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel +import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** Encapsulates app state for the lock screen bouncer. */ +@SysUISingleton +class KeyguardBouncerRepository +@Inject +constructor( + private val viewMediatorCallback: ViewMediatorCallback, + keyguardUpdateMonitor: KeyguardUpdateMonitor, +) { + var bouncerPromptReason: Int? = null + /** Determines if we want to instantaneously show the bouncer instead of translating. */ + private val _isScrimmed = MutableStateFlow(false) + val isScrimmed = _isScrimmed.asStateFlow() + /** Set amount of how much of the bouncer is showing on the screen */ + private val _expansionAmount = MutableStateFlow(EXPANSION_HIDDEN) + val expansionAmount = _expansionAmount.asStateFlow() + private val _isVisible = MutableStateFlow(false) + val isVisible = _isVisible.asStateFlow() + private val _show = MutableStateFlow<KeyguardBouncerModel?>(null) + val show = _show.asStateFlow() + private val _showingSoon = MutableStateFlow(false) + val showingSoon = _showingSoon.asStateFlow() + private val _hide = MutableStateFlow(false) + val hide = _hide.asStateFlow() + private val _startingToHide = MutableStateFlow(false) + val startingToHide = _startingToHide.asStateFlow() + private val _onDismissAction = MutableStateFlow<BouncerCallbackActionsModel?>(null) + val onDismissAction = _onDismissAction.asStateFlow() + private val _disappearAnimation = MutableStateFlow<Runnable?>(null) + val startingDisappearAnimation = _disappearAnimation.asStateFlow() + private val _keyguardPosition = MutableStateFlow(0f) + val keyguardPosition = _keyguardPosition.asStateFlow() + private val _resourceUpdateRequests = MutableStateFlow(false) + val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow() + private val _showMessage = MutableStateFlow<BouncerShowMessageModel?>(null) + val showMessage = _showMessage.asStateFlow() + private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null) + /** Determines if user is already unlocked */ + val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow() + private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null) + val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow() + private val _onScreenTurnedOff = MutableStateFlow(false) + val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow() + + val bouncerErrorMessage: CharSequence? + get() = viewMediatorCallback.consumeCustomMessage() + + init { + val callback = + object : KeyguardUpdateMonitorCallback() { + override fun onStrongAuthStateChanged(userId: Int) { + bouncerPromptReason = viewMediatorCallback.bouncerPromptReason + } + + override fun onLockedOutStateChanged(type: BiometricSourceType) { + if (type == BiometricSourceType.FINGERPRINT) { + bouncerPromptReason = viewMediatorCallback.bouncerPromptReason + } + } + } + + keyguardUpdateMonitor.registerCallback(callback) + } + + fun setScrimmed(isScrimmed: Boolean) { + _isScrimmed.value = isScrimmed + } + + fun setExpansion(expansion: Float) { + _expansionAmount.value = expansion + } + + fun setVisible(isVisible: Boolean) { + _isVisible.value = isVisible + } + + fun setShow(keyguardBouncerModel: KeyguardBouncerModel?) { + _show.value = keyguardBouncerModel + } + + fun setShowingSoon(showingSoon: Boolean) { + _showingSoon.value = showingSoon + } + + fun setHide(hide: Boolean) { + _hide.value = hide + } + + fun setStartingToHide(startingToHide: Boolean) { + _startingToHide.value = startingToHide + } + + fun setOnDismissAction(bouncerCallbackActionsModel: BouncerCallbackActionsModel?) { + _onDismissAction.value = bouncerCallbackActionsModel + } + + fun setStartDisappearAnimation(runnable: Runnable?) { + _disappearAnimation.value = runnable + } + + fun setKeyguardPosition(keyguardPosition: Float) { + _keyguardPosition.value = keyguardPosition + } + + fun setResourceUpdateRequests(willUpdateResources: Boolean) { + _resourceUpdateRequests.value = willUpdateResources + } + + fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) { + _showMessage.value = bouncerShowMessageModel + } + + fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) { + _keyguardAuthenticated.value = keyguardAuthenticated + } + + fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) { + _isBackButtonEnabled.value = isBackButtonEnabled + } + + fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) { + _onScreenTurnedOff.value = onScreenTurnedOff + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt new file mode 100644 index 000000000000..10c7a3774e09 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.view.View +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.phone.KeyguardBouncer +import com.android.systemui.util.ListenerSet +import javax.inject.Inject + +/** Interactor to add and remove callbacks for the bouncer. */ +@SysUISingleton +class BouncerCallbackInteractor @Inject constructor() { + private var resetCallbacks = ListenerSet<KeyguardBouncer.KeyguardResetCallback>() + private var expansionCallbacks = ArrayList<KeyguardBouncer.BouncerExpansionCallback>() + /** Add a KeyguardResetCallback. */ + fun addKeyguardResetCallback(callback: KeyguardBouncer.KeyguardResetCallback) { + resetCallbacks.addIfAbsent(callback) + } + + /** Remove a KeyguardResetCallback. */ + fun removeKeyguardResetCallback(callback: KeyguardBouncer.KeyguardResetCallback) { + resetCallbacks.remove(callback) + } + + /** Adds a callback to listen to bouncer expansion updates. */ + fun addBouncerExpansionCallback(callback: KeyguardBouncer.BouncerExpansionCallback) { + if (!expansionCallbacks.contains(callback)) { + expansionCallbacks.add(callback) + } + } + + /** + * Removes a previously added callback. If the callback was never added, this method does + * nothing. + */ + fun removeBouncerExpansionCallback(callback: KeyguardBouncer.BouncerExpansionCallback) { + expansionCallbacks.remove(callback) + } + + /** Propagate fully shown to bouncer expansion callbacks. */ + fun dispatchFullyShown() { + for (callback in expansionCallbacks) { + callback.onFullyShown() + } + } + + /** Propagate starting to hide to bouncer expansion callbacks. */ + fun dispatchStartingToHide() { + for (callback in expansionCallbacks) { + callback.onStartingToHide() + } + } + + /** Propagate starting to show to bouncer expansion callbacks. */ + fun dispatchStartingToShow() { + for (callback in expansionCallbacks) { + callback.onStartingToShow() + } + } + + /** Propagate fully hidden to bouncer expansion callbacks. */ + fun dispatchFullyHidden() { + for (callback in expansionCallbacks) { + callback.onFullyHidden() + } + } + + /** Propagate expansion changes to bouncer expansion callbacks. */ + fun dispatchExpansionChanged(expansion: Float) { + for (callback in expansionCallbacks) { + callback.onExpansionChanged(expansion) + } + } + /** Propagate visibility changes to bouncer expansion callbacks. */ + fun dispatchVisibilityChanged(visibility: Int) { + for (callback in expansionCallbacks) { + callback.onVisibilityChanged(visibility == View.VISIBLE) + } + } + + /** Propagate keyguard reset. */ + fun dispatchReset() { + for (callback in resetCallbacks) { + callback.onKeyguardReset() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt new file mode 100644 index 000000000000..7d4db37c6b0f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.content.res.ColorStateList +import android.os.Handler +import android.os.Trace +import android.os.UserHandle +import android.os.UserManager +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.DejankUtils +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.DismissCallbackRegistry +import com.android.systemui.keyguard.data.BouncerView +import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository +import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel +import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel +import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.shared.system.SysUiStatsLog +import com.android.systemui.statusbar.phone.KeyguardBouncer +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.policy.KeyguardStateController +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map + +/** Encapsulates business logic for interacting with the lock-screen bouncer. */ +@SysUISingleton +class BouncerInteractor +@Inject +constructor( + private val repository: KeyguardBouncerRepository, + private val bouncerView: BouncerView, + @Main private val mainHandler: Handler, + private val keyguardStateController: KeyguardStateController, + private val keyguardSecurityModel: KeyguardSecurityModel, + private val callbackInteractor: BouncerCallbackInteractor, + private val falsingCollector: FalsingCollector, + private val dismissCallbackRegistry: DismissCallbackRegistry, + keyguardBypassController: KeyguardBypassController, + keyguardUpdateMonitor: KeyguardUpdateMonitor, +) { + /** Whether we want to wait for face auth. */ + private val bouncerFaceDelay = + keyguardStateController.isFaceAuthEnabled && + !keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible( + KeyguardUpdateMonitor.getCurrentUser() + ) && + !needsFullscreenBouncer() && + !keyguardUpdateMonitor.userNeedsStrongAuth() && + !keyguardBypassController.bypassEnabled + + /** Runnable to show the bouncer. */ + val showRunnable = Runnable { + repository.setVisible(true) + repository.setShow( + KeyguardBouncerModel( + promptReason = repository.bouncerPromptReason ?: 0, + errorMessage = repository.bouncerErrorMessage, + expansionAmount = repository.expansionAmount.value + ) + ) + repository.setShowingSoon(false) + } + + val keyguardAuthenticated: Flow<Boolean> = repository.keyguardAuthenticated.filterNotNull() + val screenTurnedOff: Flow<Unit> = repository.onScreenTurnedOff.filter { it }.map {} + val show: Flow<KeyguardBouncerModel> = repository.show.filterNotNull() + val hide: Flow<Unit> = repository.hide.filter { it }.map {} + val startingToHide: Flow<Unit> = repository.startingToHide.filter { it }.map {} + val isVisible: Flow<Boolean> = repository.isVisible + val isBackButtonEnabled: Flow<Boolean> = repository.isBackButtonEnabled.filterNotNull() + val expansionAmount: Flow<Float> = repository.expansionAmount + val showMessage: Flow<BouncerShowMessageModel> = repository.showMessage.filterNotNull() + val startingDisappearAnimation: Flow<Runnable> = + repository.startingDisappearAnimation.filterNotNull() + val onDismissAction: Flow<BouncerCallbackActionsModel> = + repository.onDismissAction.filterNotNull() + val resourceUpdateRequests: Flow<Boolean> = repository.resourceUpdateRequests.filter { it } + val keyguardPosition: Flow<Float> = repository.keyguardPosition + + // TODO(b/243685699): Move isScrimmed logic to data layer. + // TODO(b/243695312): Encapsulate all of the show logic for the bouncer. + /** Show the bouncer if necessary and set the relevant states. */ + @JvmOverloads + fun show(isScrimmed: Boolean) { + // Reset some states as we show the bouncer. + repository.setShowMessage(null) + repository.setOnScreenTurnedOff(false) + repository.setKeyguardAuthenticated(null) + repository.setHide(false) + repository.setStartingToHide(false) + + val resumeBouncer = + (repository.isVisible.value || repository.showingSoon.value) && needsFullscreenBouncer() + + if (!resumeBouncer && repository.show.value != null) { + // If bouncer is visible, the bouncer is already showing. + return + } + + val keyguardUserId = KeyguardUpdateMonitor.getCurrentUser() + if (keyguardUserId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser()) { + // In split system user mode, we never unlock system user. + return + } + + Trace.beginSection("KeyguardBouncer#show") + repository.setScrimmed(isScrimmed) + if (isScrimmed) { + setExpansion(KeyguardBouncer.EXPANSION_VISIBLE) + } + + if (resumeBouncer) { + bouncerView.delegate?.resume() + // Bouncer is showing the next security screen and we just need to prompt a resume. + return + } + if (bouncerView.delegate?.showNextSecurityScreenOrFinish() == true) { + // Keyguard is done. + return + } + + repository.setShowingSoon(true) + if (bouncerFaceDelay) { + mainHandler.postDelayed(showRunnable, 1200L) + } else { + DejankUtils.postAfterTraversal(showRunnable) + } + keyguardStateController.notifyBouncerShowing(true) + callbackInteractor.dispatchStartingToShow() + + Trace.endSection() + } + + /** Sets the correct bouncer states to hide the bouncer. */ + fun hide() { + Trace.beginSection("KeyguardBouncer#hide") + if (isFullyShowing()) { + SysUiStatsLog.write( + SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED, + SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN + ) + dismissCallbackRegistry.notifyDismissCancelled() + } + + falsingCollector.onBouncerHidden() + keyguardStateController.notifyBouncerShowing(false /* showing */) + cancelShowRunnable() + repository.setShowingSoon(false) + repository.setOnDismissAction(null) + repository.setVisible(false) + repository.setHide(true) + repository.setShow(null) + Trace.endSection() + } + + /** + * Sets the panel expansion which is calculated further upstream. Expansion is from 0f to 1f + * where 0f => showing and 1f => hiding + */ + fun setExpansion(expansion: Float) { + val oldExpansion = repository.expansionAmount.value + val expansionChanged = oldExpansion != expansion + if (repository.startingDisappearAnimation.value == null) { + repository.setExpansion(expansion) + } + + if ( + expansion == KeyguardBouncer.EXPANSION_VISIBLE && + oldExpansion != KeyguardBouncer.EXPANSION_VISIBLE + ) { + falsingCollector.onBouncerShown() + callbackInteractor.dispatchFullyShown() + } else if ( + expansion == KeyguardBouncer.EXPANSION_HIDDEN && + oldExpansion != KeyguardBouncer.EXPANSION_HIDDEN + ) { + repository.setVisible(false) + repository.setShow(null) + falsingCollector.onBouncerHidden() + DejankUtils.postAfterTraversal { callbackInteractor.dispatchReset() } + callbackInteractor.dispatchFullyHidden() + } else if ( + expansion != KeyguardBouncer.EXPANSION_VISIBLE && + oldExpansion == KeyguardBouncer.EXPANSION_VISIBLE + ) { + callbackInteractor.dispatchStartingToHide() + repository.setStartingToHide(true) + } + if (expansionChanged) { + callbackInteractor.dispatchExpansionChanged(expansion) + } + } + + /** Set the initial keyguard message to show when bouncer is shown. */ + fun showMessage(message: String?, colorStateList: ColorStateList?) { + repository.setShowMessage(BouncerShowMessageModel(message, colorStateList)) + } + + /** + * Sets actions to the bouncer based on how the bouncer is dismissed. If the bouncer is + * unlocked, we will run the onDismissAction. If the bouncer is existed before unlocking, we + * call cancelAction. + */ + fun setDismissAction( + onDismissAction: ActivityStarter.OnDismissAction?, + cancelAction: Runnable? + ) { + repository.setOnDismissAction(BouncerCallbackActionsModel(onDismissAction, cancelAction)) + } + + /** Update the resources of the views. */ + fun updateResources() { + repository.setResourceUpdateRequests(true) + } + + /** Tell the bouncer that keyguard is authenticated. */ + fun notifyKeyguardAuthenticated(strongAuth: Boolean) { + repository.setKeyguardAuthenticated(strongAuth) + } + + /** Tell the bouncer the screen has turned off. */ + fun onScreenTurnedOff() { + repository.setOnScreenTurnedOff(true) + } + + /** Update the position of the bouncer when showing. */ + fun setKeyguardPosition(position: Float) { + repository.setKeyguardPosition(position) + } + + /** Notifies that the state change was handled. */ + fun notifyKeyguardAuthenticatedHandled() { + repository.setKeyguardAuthenticated(null) + } + + /** Notify that view visibility has changed. */ + fun notifyBouncerVisibilityHasChanged(visibility: Int) { + callbackInteractor.dispatchVisibilityChanged(visibility) + } + + /** Notify that the resources have been updated */ + fun notifyUpdatedResources() { + repository.setResourceUpdateRequests(false) + } + + /** Set whether back button is enabled when on the bouncer screen. */ + fun setBackButtonEnabled(enabled: Boolean) { + repository.setIsBackButtonEnabled(enabled) + } + + /** Tell the bouncer to start the pre hide animation. */ + fun startDisappearAnimation(runnable: Runnable) { + val finishRunnable = Runnable { + repository.setStartDisappearAnimation(null) + runnable.run() + } + repository.setStartDisappearAnimation(finishRunnable) + } + + /** Returns whether bouncer is fully showing. */ + fun isFullyShowing(): Boolean { + return (repository.showingSoon.value || repository.isVisible.value) && + repository.expansionAmount.value == KeyguardBouncer.EXPANSION_VISIBLE && + repository.startingDisappearAnimation.value == null + } + + /** Returns whether bouncer is scrimmed. */ + fun isScrimmed(): Boolean { + return repository.isScrimmed.value + } + + /** If bouncer expansion is between 0f and 1f non-inclusive. */ + fun isInTransit(): Boolean { + return repository.showingSoon.value || + repository.expansionAmount.value != KeyguardBouncer.EXPANSION_HIDDEN && + repository.expansionAmount.value != KeyguardBouncer.EXPANSION_VISIBLE + } + + /** Return whether bouncer is animating away. */ + fun isAnimatingAway(): Boolean { + return repository.startingDisappearAnimation.value != null + } + + /** Return whether bouncer will dismiss with actions */ + fun willDismissWithAction(): Boolean { + return repository.onDismissAction.value?.onDismissAction != null + } + + /** Returns whether the bouncer should be full screen. */ + private fun needsFullscreenBouncer(): Boolean { + val mode: KeyguardSecurityModel.SecurityMode = + keyguardSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()) + return mode == KeyguardSecurityModel.SecurityMode.SimPin || + mode == KeyguardSecurityModel.SecurityMode.SimPuk + } + + /** Remove the show runnable from the main handler queue to improve performance. */ + private fun cancelShowRunnable() { + DejankUtils.removeCallbacks(showRunnable) + mainHandler.removeCallbacks(showRunnable) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt new file mode 100644 index 000000000000..81cf5b41ea71 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.shared.model + +import com.android.systemui.plugins.ActivityStarter + +/** Encapsulates callbacks to be invoked by the bouncer logic. */ +// TODO(b/243683121): Move dismiss logic from view controllers +data class BouncerCallbackActionsModel( + val onDismissAction: ActivityStarter.OnDismissAction?, + val cancelAction: Runnable? +) diff --git a/core/java/android/app/cloudsearch/SearchResponse.aidl b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt index 2064d112c6b9..05cdeaaa106d 100644 --- a/core/java/android/app/cloudsearch/SearchResponse.aidl +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt @@ -1,19 +1,22 @@ -/** - * Copyright (c) 2022, The Android Open Source Project +/* + * Copyright (C) 2022 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 + * 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. + * limitations under the License */ -package android.app.cloudsearch; +package com.android.systemui.keyguard.shared.model -parcelable SearchResponse;
\ No newline at end of file +import android.content.res.ColorStateList + +/** Show a keyguard message to the bouncer. */ +data class BouncerShowMessageModel(val message: String?, val colorStateList: ColorStateList?) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBouncerModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBouncerModel.kt new file mode 100644 index 000000000000..ad783da7f304 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBouncerModel.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.shared.model + +/** Models the state of the lock-screen bouncer */ +data class KeyguardBouncerModel( + val promptReason: Int = 0, + val errorMessage: CharSequence? = null, + val expansionAmount: Float = 0f, +) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt new file mode 100644 index 000000000000..df260148751c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.ui.binder + +import android.view.KeyEvent +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.internal.policy.SystemBarUtils +import com.android.keyguard.KeyguardHostViewController +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.keyguard.dagger.KeyguardBouncerComponent +import com.android.systemui.keyguard.data.BouncerViewDelegate +import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE +import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.launch + +/** Binds the bouncer container to its view model. */ +object KeyguardBouncerViewBinder { + @JvmStatic + fun bind( + view: ViewGroup, + viewModel: KeyguardBouncerViewModel, + componentFactory: KeyguardBouncerComponent.Factory + ) { + // Builds the KeyguardHostViewController from bouncer view group. + val hostViewController: KeyguardHostViewController = + componentFactory.create(view).keyguardHostViewController + hostViewController.init() + val delegate = + object : BouncerViewDelegate { + override fun isFullScreenBouncer(): Boolean { + val mode = hostViewController.currentSecurityMode + return mode == KeyguardSecurityModel.SecurityMode.SimPin || + mode == KeyguardSecurityModel.SecurityMode.SimPuk + } + + override fun shouldDismissOnMenuPressed(): Boolean { + return hostViewController.shouldEnableMenuKey() + } + + override fun interceptMediaKey(event: KeyEvent?): Boolean { + return hostViewController.interceptMediaKey(event) + } + + override fun dispatchBackKeyEventPreIme(): Boolean { + return hostViewController.dispatchBackKeyEventPreIme() + } + + override fun showNextSecurityScreenOrFinish(): Boolean { + return hostViewController.dismiss(KeyguardUpdateMonitor.getCurrentUser()) + } + + override fun resume() { + hostViewController.showPrimarySecurityScreen() + hostViewController.onResume() + } + } + view.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.STARTED) { + try { + viewModel.setBouncerViewDelegate(delegate) + launch { + viewModel.show.collect { + hostViewController.showPrimarySecurityScreen() + hostViewController.appear( + SystemBarUtils.getStatusBarHeight(view.context) + ) + } + } + + launch { + viewModel.showPromptReason.collect { prompt -> + hostViewController.showPromptReason(prompt) + } + } + + launch { + viewModel.showBouncerErrorMessage.collect { errorMessage -> + hostViewController.showErrorMessage(errorMessage) + } + } + + launch { + viewModel.showWithFullExpansion.collect { model -> + hostViewController.resetSecurityContainer() + hostViewController.showPromptReason(model.promptReason) + hostViewController.onResume() + } + } + + launch { + viewModel.hide.collect { + hostViewController.cancelDismissAction() + hostViewController.cleanUp() + hostViewController.resetSecurityContainer() + } + } + + launch { + viewModel.startingToHide.collect { hostViewController.onStartingToHide() } + } + + launch { + viewModel.setDismissAction.collect { + hostViewController.setOnDismissAction( + it.onDismissAction, + it.cancelAction + ) + } + } + + launch { + viewModel.startDisappearAnimation.collect { + hostViewController.startDisappearAnimation(it) + } + } + + launch { + viewModel.bouncerExpansionAmount.collect { expansion -> + hostViewController.setExpansion(expansion) + } + } + + launch { + viewModel.bouncerExpansionAmount + .filter { it == EXPANSION_VISIBLE } + .collect { + hostViewController.onResume() + view.announceForAccessibility( + hostViewController.accessibilityTitleForCurrentMode + ) + } + } + + launch { + viewModel.isBouncerVisible.collect { isVisible -> + val visibility = if (isVisible) View.VISIBLE else View.INVISIBLE + view.visibility = visibility + hostViewController.onBouncerVisibilityChanged(visibility) + viewModel.notifyBouncerVisibilityHasChanged(visibility) + } + } + + launch { + viewModel.isBouncerVisible + .filter { !it } + .collect { + // Remove existing input for security reasons. + hostViewController.resetSecurityContainer() + } + } + + launch { + viewModel.keyguardPosition.collect { position -> + hostViewController.updateKeyguardPosition(position) + } + } + + launch { + viewModel.updateResources.collect { + hostViewController.updateResources() + viewModel.notifyUpdateResources() + } + } + + launch { + viewModel.bouncerShowMessage.collect { + hostViewController.showMessage(it.message, it.colorStateList) + } + } + + launch { + viewModel.keyguardAuthenticated.collect { + hostViewController.finish(it, KeyguardUpdateMonitor.getCurrentUser()) + viewModel.notifyKeyguardAuthenticated() + } + } + + launch { + viewModel + .observeOnIsBackButtonEnabled { view.systemUiVisibility } + .collect { view.systemUiVisibility = it } + } + + launch { + viewModel.screenTurnedOff.collect { + if (view.visibility == View.VISIBLE) { + hostViewController.onPause() + } + } + } + awaitCancellation() + } finally { + viewModel.setBouncerViewDelegate(null) + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt new file mode 100644 index 000000000000..9ad52117bfc6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.ui.viewmodel + +import android.view.View +import com.android.systemui.keyguard.data.BouncerView +import com.android.systemui.keyguard.data.BouncerViewDelegate +import com.android.systemui.keyguard.domain.interactor.BouncerInteractor +import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel +import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel +import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel +import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map + +/** Models UI state for the lock screen bouncer; handles user input. */ +class KeyguardBouncerViewModel +@Inject +constructor( + private val view: BouncerView, + private val interactor: BouncerInteractor, +) { + /** Observe on bouncer expansion amount. */ + val bouncerExpansionAmount: Flow<Float> = interactor.expansionAmount + + /** Observe on bouncer visibility. */ + val isBouncerVisible: Flow<Boolean> = interactor.isVisible + + /** Observe whether bouncer is showing. */ + val show: Flow<KeyguardBouncerModel> = interactor.show + + /** Observe bouncer prompt when bouncer is showing. */ + val showPromptReason: Flow<Int> = interactor.show.map { it.promptReason } + + /** Observe bouncer error message when bouncer is showing. */ + val showBouncerErrorMessage: Flow<CharSequence> = + interactor.show.map { it.errorMessage }.filterNotNull() + + /** Observe visible expansion when bouncer is showing. */ + val showWithFullExpansion: Flow<KeyguardBouncerModel> = + interactor.show.filter { it.expansionAmount == EXPANSION_VISIBLE } + + /** Observe whether bouncer is hiding. */ + val hide: Flow<Unit> = interactor.hide + + /** Observe whether bouncer is starting to hide. */ + val startingToHide: Flow<Unit> = interactor.startingToHide + + /** Observe whether we want to set the dismiss action to the bouncer. */ + val setDismissAction: Flow<BouncerCallbackActionsModel> = interactor.onDismissAction + + /** Observe whether we want to start the disappear animation. */ + val startDisappearAnimation: Flow<Runnable> = interactor.startingDisappearAnimation + + /** Observe whether we want to update keyguard position. */ + val keyguardPosition: Flow<Float> = interactor.keyguardPosition + + /** Observe whether we want to update resources. */ + val updateResources: Flow<Boolean> = interactor.resourceUpdateRequests + + /** Observe whether we want to set a keyguard message when the bouncer shows. */ + val bouncerShowMessage: Flow<BouncerShowMessageModel> = interactor.showMessage + + /** Observe whether keyguard is authenticated already. */ + val keyguardAuthenticated: Flow<Boolean> = interactor.keyguardAuthenticated + + /** Observe whether screen is turned off. */ + val screenTurnedOff: Flow<Unit> = interactor.screenTurnedOff + + /** Notify that view visibility has changed. */ + fun notifyBouncerVisibilityHasChanged(visibility: Int) { + return interactor.notifyBouncerVisibilityHasChanged(visibility) + } + /** Observe whether we want to update resources. */ + fun notifyUpdateResources() { + interactor.notifyUpdatedResources() + } + + /** Notify that keyguard authenticated was handled */ + fun notifyKeyguardAuthenticated() { + interactor.notifyKeyguardAuthenticatedHandled() + } + + /** Observe whether back button is enabled. */ + fun observeOnIsBackButtonEnabled(systemUiVisibility: () -> Int): Flow<Int> { + return interactor.isBackButtonEnabled.map { enabled -> + var vis: Int = systemUiVisibility() + vis = + if (enabled) { + vis and View.STATUS_BAR_DISABLE_BACK.inv() + } else { + vis or View.STATUS_BAR_DISABLE_BACK + } + vis + } + } + + /** Set an abstraction that will hold reference to the ui delegate for the bouncer view. */ + fun setBouncerViewDelegate(delegate: BouncerViewDelegate?) { + view.delegate = delegate + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index e25f5dabe5a9..f8c6a5791839 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -1,5 +1,6 @@ package com.android.systemui.media +import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.res.ColorStateList @@ -945,6 +946,11 @@ class MediaCarouselController @Inject constructor( mediaManager.onSwipeToDismiss() } + fun getCurrentVisibleMediaContentIntent(): PendingIntent? { + return MediaPlayerData.playerKeys() + .elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)?.data?.clickIntent + } + override fun dump(pw: PrintWriter, args: Array<out String>) { pw.apply { println("keysNeedRemoval: $keysNeedRemoval") diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt index 267c1f55a681..b3a4ddf8ec1f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt @@ -188,24 +188,28 @@ class MediaDeviceManager @Inject constructor( @AnyThread fun start() = bgExecutor.execute { - localMediaManager.registerCallback(this) - localMediaManager.startScan() - muteAwaitConnectionManager?.startListening() - playbackType = controller?.playbackInfo?.playbackType ?: PLAYBACK_TYPE_UNKNOWN - controller?.registerCallback(this) - updateCurrent() - started = true - configurationController.addCallback(configListener) + if (!started) { + localMediaManager.registerCallback(this) + localMediaManager.startScan() + muteAwaitConnectionManager?.startListening() + playbackType = controller?.playbackInfo?.playbackType ?: PLAYBACK_TYPE_UNKNOWN + controller?.registerCallback(this) + updateCurrent() + started = true + configurationController.addCallback(configListener) + } } @AnyThread fun stop() = bgExecutor.execute { - started = false - controller?.unregisterCallback(this) - localMediaManager.stopScan() - localMediaManager.unregisterCallback(this) - muteAwaitConnectionManager?.stopListening() - configurationController.removeCallback(configListener) + if (started) { + started = false + controller?.unregisterCallback(this) + localMediaManager.stopScan() + localMediaManager.unregisterCallback(this) + muteAwaitConnectionManager?.stopListening() + configurationController.removeCallback(configListener) + } } fun dump(pw: PrintWriter) { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt index 0f1ee31e066e..c6bd777fbd7a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt @@ -24,22 +24,45 @@ import android.os.Bundle import android.os.IBinder import android.os.ResultReceiver import android.os.UserHandle -import android.widget.ImageView +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.android.internal.annotations.VisibleForTesting import com.android.internal.app.ChooserActivity import com.android.internal.app.ResolverListController import com.android.internal.app.chooser.NotSelectableTargetInfo import com.android.internal.app.chooser.TargetInfo -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.util.AsyncActivityLauncher import com.android.systemui.R -import com.android.internal.R as AndroidR +import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorController +import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorView +import com.android.systemui.mediaprojection.appselector.data.RecentTask +import com.android.systemui.mediaprojection.appselector.view.RecentTasksAdapter +import com.android.systemui.mediaprojection.appselector.view.RecentTasksAdapter.RecentTaskClickListener +import com.android.systemui.util.AsyncActivityLauncher +import com.android.systemui.util.recycler.HorizontalSpacerItemDecoration +import javax.inject.Inject -class MediaProjectionAppSelectorActivity constructor( +class MediaProjectionAppSelectorActivity( private val activityLauncher: AsyncActivityLauncher, + private val controller: MediaProjectionAppSelectorController, + private val recentTasksAdapterFactory: RecentTasksAdapter.Factory, /** This is used to override the dependency in a screenshot test */ @VisibleForTesting - private val listControllerFactory: ((userHandle: UserHandle) -> ResolverListController)? = null -) : ChooserActivity() { + private val listControllerFactory: ((userHandle: UserHandle) -> ResolverListController)? +) : ChooserActivity(), MediaProjectionAppSelectorView, RecentTaskClickListener { + + @Inject + constructor( + activityLauncher: AsyncActivityLauncher, + controller: MediaProjectionAppSelectorController, + recentTasksAdapterFactory: RecentTasksAdapter.Factory, + ) : this(activityLauncher, controller, recentTasksAdapterFactory, null) + + private var recentsRoot: ViewGroup? = null + private var recentsProgress: View? = null + private var recentsRecycler: RecyclerView? = null override fun getLayoutResource() = R.layout.media_projection_app_selector @@ -52,10 +75,30 @@ class MediaProjectionAppSelectorActivity constructor( // TODO(b/240939253): update copies val title = getString(R.string.media_projection_dialog_service_title) intent.putExtra(Intent.EXTRA_TITLE, title) - super.onCreate(bundle) + controller.init(this) + } - requireViewById<ImageView>(AndroidR.id.icon).setImageResource(R.drawable.ic_present_to_all) + private fun createRecentsView(parent: ViewGroup): ViewGroup { + val recentsRoot = LayoutInflater.from(this) + .inflate(R.layout.media_projection_recent_tasks, parent, + /* attachToRoot= */ false) as ViewGroup + + recentsProgress = recentsRoot.requireViewById(R.id.media_projection_recent_tasks_loader) + recentsRecycler = recentsRoot.requireViewById(R.id.media_projection_recent_tasks_recycler) + recentsRecycler?.layoutManager = LinearLayoutManager( + this, LinearLayoutManager.HORIZONTAL, + /* reverseLayout= */false + ) + + val itemDecoration = HorizontalSpacerItemDecoration( + resources.getDimensionPixelOffset( + R.dimen.media_projection_app_selector_recents_padding + ) + ) + recentsRecycler?.addItemDecoration(itemDecoration) + + return recentsRoot } override fun appliedThemeResId(): Int = @@ -108,6 +151,7 @@ class MediaProjectionAppSelectorActivity constructor( override fun onDestroy() { activityLauncher.destroy() + controller.destroy() super.onDestroy() } @@ -115,6 +159,27 @@ class MediaProjectionAppSelectorActivity constructor( // do nothing } + override fun bind(recentTasks: List<RecentTask>) { + val recents = recentsRoot ?: return + val progress = recentsProgress ?: return + val recycler = recentsRecycler ?: return + + if (recentTasks.isEmpty()) { + recents.visibility = View.GONE + return + } + + progress.visibility = View.GONE + recycler.visibility = View.VISIBLE + recents.visibility = View.VISIBLE + + recycler.adapter = recentTasksAdapterFactory.create(recentTasks, this) + } + + override fun onRecentClicked(task: RecentTask, view: View) { + // TODO(b/240924732) Handle clicking on a recent task + } + private fun onTargetActivityLaunched(launchToken: IBinder) { if (intent.hasExtra(EXTRA_CAPTURE_REGION_RESULT_RECEIVER)) { // The client requested to return the result in the result receiver instead of @@ -145,6 +210,14 @@ class MediaProjectionAppSelectorActivity constructor( override fun shouldGetOnlyDefaultActivities() = false + // TODO(b/240924732) flip the flag when the recents selector is ready + override fun shouldShowContentPreview() = false + + override fun createContentPreviewView(parent: ViewGroup): ViewGroup = + recentsRoot ?: createRecentsView(parent).also { + recentsRoot = it + } + companion object { /** * When EXTRA_CAPTURE_REGION_RESULT_RECEIVER is passed as intent extra diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt index 969699834024..185b4fca87d0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt @@ -17,13 +17,29 @@ package com.android.systemui.media.dagger import android.app.Activity +import android.content.ComponentName +import android.content.Context +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.media.MediaProjectionAppSelectorActivity -import com.android.systemui.util.AsyncActivityLauncher +import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorController +import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader +import com.android.systemui.mediaprojection.appselector.data.AppIconLoader +import com.android.systemui.mediaprojection.appselector.data.IconLoaderLibAppIconLoader +import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider +import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader +import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider import dagger.Binds import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import javax.inject.Qualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class MediaProjectionAppSelector @Module abstract class MediaProjectionModule { @@ -31,17 +47,43 @@ abstract class MediaProjectionModule { @Binds @IntoMap @ClassKey(MediaProjectionAppSelectorActivity::class) - abstract fun bindMediaProjectionAppSelectorActivity( - activity: MediaProjectionAppSelectorActivity): Activity + abstract fun provideMediaProjectionAppSelectorActivity( + activity: MediaProjectionAppSelectorActivity + ): Activity + + @Binds + abstract fun bindRecentTaskThumbnailLoader( + impl: ActivityTaskManagerThumbnailLoader + ): RecentTaskThumbnailLoader + + @Binds + abstract fun bindRecentTaskListProvider( + impl: ShellRecentTaskListProvider + ): RecentTaskListProvider + + @Binds + abstract fun bindAppIconLoader(impl: IconLoaderLibAppIconLoader): AppIconLoader companion object { @Provides - fun provideMediaProjectionAppSelectorActivity( - activityLauncher: AsyncActivityLauncher - ): MediaProjectionAppSelectorActivity { - return MediaProjectionAppSelectorActivity( - activityLauncher + fun provideController( + recentTaskListProvider: RecentTaskListProvider, + context: Context, + @MediaProjectionAppSelector scope: CoroutineScope + ): MediaProjectionAppSelectorController { + val appSelectorComponentName = + ComponentName(context, MediaProjectionAppSelectorActivity::class.java) + + return MediaProjectionAppSelectorController( + recentTaskListProvider, + scope, + appSelectorComponentName ) } + + @MediaProjectionAppSelector + @Provides + fun provideCoroutineScope(@Application applicationScope: CoroutineScope): CoroutineScope = + CoroutineScope(applicationScope.coroutineContext + SupervisorJob()) } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java index 85d8f3fdd20a..a9e1a4d325d2 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java @@ -99,6 +99,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements private Button mStopButton; private Button mAppButton; private int mListMaxHeight; + private int mItemHeight; private WallpaperColors mWallpaperColors; private Executor mExecutor; private boolean mShouldLaunchLeBroadcastDialog; @@ -106,10 +107,12 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements MediaOutputBaseAdapter mAdapter; private final ViewTreeObserver.OnGlobalLayoutListener mDeviceListLayoutListener = () -> { + ViewGroup.LayoutParams params = mDeviceListLayout.getLayoutParams(); + int totalItemsHeight = mAdapter.getItemCount() * mItemHeight; + int correctHeight = Math.min(totalItemsHeight, mListMaxHeight); // Set max height for list - if (mDeviceListLayout.getHeight() > mListMaxHeight) { - ViewGroup.LayoutParams params = mDeviceListLayout.getLayoutParams(); - params.height = mListMaxHeight; + if (correctHeight != params.height) { + params.height = correctHeight; mDeviceListLayout.setLayoutParams(params); } }; @@ -212,6 +215,8 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements mLayoutManager = new LayoutManagerWrapper(mContext); mListMaxHeight = context.getResources().getDimensionPixelSize( R.dimen.media_output_dialog_list_max_height); + mItemHeight = context.getResources().getDimensionPixelSize( + R.dimen.media_output_dialog_list_item_height); mExecutor = Executors.newSingleThreadExecutor(); } @@ -246,8 +251,10 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements mDeviceListLayout.getViewTreeObserver().addOnGlobalLayoutListener( mDeviceListLayoutListener); // Init device list + mLayoutManager.setAutoMeasureEnabled(true); mDevicesRecyclerView.setLayoutManager(mLayoutManager); mDevicesRecyclerView.setAdapter(mAdapter); + mDevicesRecyclerView.setHasFixedSize(false); // Init header icon mHeaderIcon.setOnClickListener(v -> onHeaderIconClick()); // Init bottom buttons diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index 7b4ac1214a7c..19b401d80600 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -125,13 +125,16 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, private final NearbyMediaDevicesManager mNearbyMediaDevicesManager; private final Map<String, Integer> mNearbyDeviceInfoMap = new ConcurrentHashMap<>(); - private boolean mIsRefreshing = false; - private boolean mNeedRefresh = false; + @VisibleForTesting + boolean mIsRefreshing = false; + @VisibleForTesting + boolean mNeedRefresh = false; private MediaController mMediaController; @VisibleForTesting Callback mCallback; @VisibleForTesting LocalMediaManager mLocalMediaManager; + @VisibleForTesting private MediaOutputMetricLogger mMetricLogger; private int mCurrentState; @@ -221,15 +224,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, Log.d(TAG, "No media controller for " + mPackageName); } } - if (mLocalMediaManager == null) { - if (DEBUG) { - Log.d(TAG, "No local media manager " + mPackageName); - } - return; - } mCallback = cb; - mLocalMediaManager.unregisterCallback(this); - mLocalMediaManager.stopScan(); mLocalMediaManager.registerCallback(this); mLocalMediaManager.startScan(); } @@ -251,10 +246,8 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, if (mMediaController != null) { mMediaController.unregisterCallback(mCb); } - if (mLocalMediaManager != null) { - mLocalMediaManager.unregisterCallback(this); - mLocalMediaManager.stopScan(); - } + mLocalMediaManager.unregisterCallback(this); + mLocalMediaManager.stopScan(); synchronized (mMediaDevicesLock) { mCachedMediaDevices.clear(); mMediaDevices.clear(); @@ -658,10 +651,6 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, return mLocalMediaManager.getCurrentConnectedDevice(); } - private MediaDevice getMediaDeviceById(String id) { - return mLocalMediaManager.getMediaDeviceById(new ArrayList<>(mMediaDevices), id); - } - boolean addDeviceToPlayMedia(MediaDevice device) { mMetricLogger.logInteractionExpansion(device); return mLocalMediaManager.addDeviceToPlayMedia(device); @@ -683,10 +672,6 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, return mLocalMediaManager.getDeselectableMediaDevice(); } - void adjustSessionVolume(String sessionId, int volume) { - mLocalMediaManager.adjustSessionVolume(sessionId, volume); - } - void adjustSessionVolume(int volume) { mLocalMediaManager.adjustSessionVolume(volume); } @@ -1013,7 +998,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, return; } - if (newState == PlaybackState.STATE_STOPPED || newState == PlaybackState.STATE_PAUSED) { + if (newState == PlaybackState.STATE_STOPPED) { mCallback.onMediaStoppedOrPaused(); } mCurrentState = newState; diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java index dc1488eefc4e..53b4d434bfcb 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java +++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java @@ -16,7 +16,7 @@ package com.android.systemui.media.dream; -import static com.android.systemui.flags.Flags.MEDIA_DREAM_COMPLICATION; +import static com.android.systemui.flags.Flags.DREAM_MEDIA_COMPLICATION; import android.content.Context; import android.util.Log; @@ -77,7 +77,7 @@ public class MediaDreamSentinel extends CoreStartable { public void onMediaDataLoaded(@NonNull String key, @Nullable String oldKey, @NonNull MediaData data, boolean immediately, int receivedSmartspaceCardLatency, boolean isSsReactivated) { - if (!mFeatureFlags.isEnabled(MEDIA_DREAM_COMPLICATION)) { + if (!mFeatureFlags.isEnabled(DREAM_MEDIA_COMPLICATION)) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt new file mode 100644 index 000000000000..2b381a954e27 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.mediaprojection.appselector + +import android.content.ComponentName +import com.android.systemui.media.dagger.MediaProjectionAppSelector +import com.android.systemui.mediaprojection.appselector.data.RecentTask +import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch + +class MediaProjectionAppSelectorController( + private val recentTaskListProvider: RecentTaskListProvider, + @MediaProjectionAppSelector private val scope: CoroutineScope, + private val appSelectorComponentName: ComponentName +) { + + fun init(view: MediaProjectionAppSelectorView) { + scope.launch { + val tasks = recentTaskListProvider.loadRecentTasks().sortTasks() + view.bind(tasks) + } + } + + fun destroy() { + scope.cancel() + } + + private fun List<RecentTask>.sortTasks(): List<RecentTask> = + sortedBy { + // Show normal tasks first and only then tasks with opened app selector + it.topActivityComponent == appSelectorComponentName + } +} diff --git a/core/java/android/app/cloudsearch/SearchRequest.aidl b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorView.kt index 9f2cdb888bdf..6550aa5569e4 100644 --- a/core/java/android/app/cloudsearch/SearchRequest.aidl +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorView.kt @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2022, The Android Open Source Project +/* + * Copyright (C) 2022 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 + * 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, @@ -14,6 +14,10 @@ * limitations under the License. */ -package android.app.cloudsearch; +package com.android.systemui.mediaprojection.appselector -parcelable SearchRequest; +import com.android.systemui.mediaprojection.appselector.data.RecentTask + +interface MediaProjectionAppSelectorView { + fun bind(recentTasks: List<RecentTask>) +} diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt new file mode 100644 index 000000000000..0927f3b00724 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.mediaprojection.appselector.data + +import android.content.ComponentName +import android.content.Context +import android.content.pm.PackageManager +import android.content.pm.PackageManager.ComponentInfoFlags +import android.graphics.drawable.Drawable +import android.os.UserHandle +import com.android.launcher3.icons.BaseIconFactory.IconOptions +import com.android.launcher3.icons.IconFactory +import com.android.systemui.dagger.qualifiers.Background +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext + +interface AppIconLoader { + suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? +} + +class IconLoaderLibAppIconLoader +@Inject +constructor( + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val context: Context, + private val packageManager: PackageManager +) : AppIconLoader { + + override suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? = + withContext(backgroundDispatcher) { + IconFactory.obtain(context).use<IconFactory, Drawable?> { iconFactory -> + val activityInfo = packageManager + .getActivityInfo(component, ComponentInfoFlags.of(0)) + val icon = activityInfo.loadIcon(packageManager) ?: return@withContext null + val userHandler = UserHandle.of(userId) + val options = IconOptions().apply { setUser(userHandler) } + val badgedIcon = iconFactory.createBadgedIconBitmap(icon, options) + badgedIcon.newIcon(context) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt new file mode 100644 index 000000000000..cd994b857e95 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.mediaprojection.appselector.data + +import android.annotation.ColorInt +import android.content.ComponentName + +data class RecentTask( + val taskId: Int, + val userId: Int, + val topActivityComponent: ComponentName?, + val baseIntentComponent: ComponentName?, + @ColorInt val colorBackground: Int? +) diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt new file mode 100644 index 000000000000..e8b49cd8ec1c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.mediaprojection.appselector.data + +import android.app.ActivityManager +import android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.util.kotlin.getOrNull +import com.android.wm.shell.recents.RecentTasks +import com.android.wm.shell.util.GroupedRecentTaskInfo +import java.util.Optional +import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext +import java.util.concurrent.Executor + +interface RecentTaskListProvider { + /** Loads recent tasks, the returned task list is from the most-recent to least-recent order */ + suspend fun loadRecentTasks(): List<RecentTask> +} + +class ShellRecentTaskListProvider +@Inject +constructor( + @Background private val coroutineDispatcher: CoroutineDispatcher, + @Background private val backgroundExecutor: Executor, + private val recentTasks: Optional<RecentTasks> +) : RecentTaskListProvider { + + private val recents by lazy { recentTasks.getOrNull() } + + override suspend fun loadRecentTasks(): List<RecentTask> = + withContext(coroutineDispatcher) { + val rawRecentTasks: List<GroupedRecentTaskInfo> = recents?.getTasks() ?: emptyList() + + rawRecentTasks + .flatMap { listOfNotNull(it.taskInfo1, it.taskInfo2) } + .map { + RecentTask( + it.taskId, + it.userId, + it.topActivity, + it.baseIntent?.component, + it.taskDescription?.backgroundColor + ) + } + } + + private suspend fun RecentTasks.getTasks(): List<GroupedRecentTaskInfo> = + suspendCoroutine { continuation -> + getRecentTasks( + Integer.MAX_VALUE, + RECENT_IGNORE_UNAVAILABLE, + ActivityManager.getCurrentUser(), + backgroundExecutor + ) { tasks -> + continuation.resume(tasks) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt new file mode 100644 index 000000000000..47faaed10302 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.mediaprojection.appselector.data + +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.shared.recents.model.ThumbnailData +import com.android.systemui.shared.system.ActivityManagerWrapper +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext + +interface RecentTaskThumbnailLoader { + suspend fun loadThumbnail(taskId: Int): ThumbnailData? +} + +class ActivityTaskManagerThumbnailLoader +@Inject +constructor( + @Background private val coroutineDispatcher: CoroutineDispatcher, + private val activityManager: ActivityManagerWrapper +) : RecentTaskThumbnailLoader { + + override suspend fun loadThumbnail(taskId: Int): ThumbnailData? = + withContext(coroutineDispatcher) { + val thumbnailData = + activityManager.getTaskThumbnail(taskId, /* isLowResolution= */ false) + if (thumbnailData.thumbnail == null) null else thumbnailData + } +} diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt new file mode 100644 index 000000000000..ec5abc7a12f4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.mediaprojection.appselector.view + +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.recyclerview.widget.RecyclerView +import com.android.systemui.R +import com.android.systemui.media.dagger.MediaProjectionAppSelector +import com.android.systemui.mediaprojection.appselector.data.AppIconLoader +import com.android.systemui.mediaprojection.appselector.data.RecentTask +import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch + +class RecentTaskViewHolder @AssistedInject constructor( + @Assisted root: ViewGroup, + private val iconLoader: AppIconLoader, + private val thumbnailLoader: RecentTaskThumbnailLoader, + @MediaProjectionAppSelector private val scope: CoroutineScope +) : RecyclerView.ViewHolder(root) { + + private val iconView: ImageView = root.requireViewById(R.id.task_icon) + private val thumbnailView: ImageView = root.requireViewById(R.id.task_thumbnail) + + private var job: Job? = null + + fun bind(task: RecentTask, onClick: (View) -> Unit) { + job?.cancel() + + job = + scope.launch { + task.baseIntentComponent?.let { component -> + launch { + val icon = iconLoader.loadIcon(task.userId, component) + iconView.setImageDrawable(icon) + } + } + launch { + val thumbnail = thumbnailLoader.loadThumbnail(task.taskId) + thumbnailView.setImageBitmap(thumbnail?.thumbnail) + } + } + + thumbnailView.setOnClickListener(onClick) + } + + fun onRecycled() { + iconView.setImageDrawable(null) + thumbnailView.setImageBitmap(null) + job?.cancel() + job = null + } + + @AssistedFactory + fun interface Factory { + fun create(root: ViewGroup): RecentTaskViewHolder + } +} diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt new file mode 100644 index 000000000000..ec9cfa88f34d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.mediaprojection.appselector.view + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.android.systemui.R +import com.android.systemui.mediaprojection.appselector.data.RecentTask +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +class RecentTasksAdapter @AssistedInject constructor( + @Assisted private val items: List<RecentTask>, + @Assisted private val listener: RecentTaskClickListener, + private val viewHolderFactory: RecentTaskViewHolder.Factory +) : RecyclerView.Adapter<RecentTaskViewHolder>() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecentTaskViewHolder { + val taskItem = + LayoutInflater.from(parent.context) + .inflate(R.layout.media_projection_task_item, null) as ViewGroup + + return viewHolderFactory.create(taskItem) + } + + override fun onBindViewHolder(holder: RecentTaskViewHolder, position: Int) { + val task = items[position] + holder.bind(task, onClick = { + listener.onRecentClicked(task, holder.itemView) + }) + } + + override fun getItemCount(): Int = items.size + + override fun onViewRecycled(holder: RecentTaskViewHolder) { + holder.onRecycled() + } + + interface RecentTaskClickListener { + fun onRecentClicked(task: RecentTask, view: View) + } + + @AssistedFactory + fun interface Factory { + fun create(items: List<RecentTask>, listener: RecentTaskClickListener): RecentTasksAdapter + } +} diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 30947e839f0a..50a10bc0b15a 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -541,10 +541,12 @@ public class NavigationBar extends ViewController<NavigationBarView> implements if (!mImeVisible) { // IME not showing, take all touches info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); + return; } if (!mView.isImeRenderingNavButtons()) { // IME showing but not drawing any buttons, take all touches info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); + return; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java index cd3609108c28..1d058744ac94 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java @@ -28,21 +28,29 @@ import android.widget.ScrollView; public class NonInterceptingScrollView extends ScrollView { private final int mTouchSlop; + private float mDownY; private boolean mScrollEnabled = true; + private boolean mPreventingIntercept; public NonInterceptingScrollView(Context context, AttributeSet attrs) { super(context, attrs); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } + public boolean isPreventingIntercept() { + return mPreventingIntercept; + } + @Override public boolean onTouchEvent(MotionEvent ev) { int action = ev.getActionMasked(); switch (action) { case MotionEvent.ACTION_DOWN: + mPreventingIntercept = false; if (canScrollVertically(1)) { // If we can scroll down, make sure we're not intercepted by the parent + mPreventingIntercept = true; final ViewParent parent = getParent(); if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); @@ -62,10 +70,13 @@ public class NonInterceptingScrollView extends ScrollView { public boolean onInterceptTouchEvent(MotionEvent ev) { // If there's a touch on this view and we can scroll down, we don't want to be intercepted int action = ev.getActionMasked(); + switch (action) { case MotionEvent.ACTION_DOWN: - // If we can scroll down, make sure non of our parents intercepts us. + mPreventingIntercept = false; + // If we can scroll down, make sure none of our parents intercepts us. if (canScrollVertically(1)) { + mPreventingIntercept = true; final ViewParent parent = getParent(); if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index 7b1ddd62ec6e..ef87fb49752d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -131,6 +131,10 @@ public class QSContainerImpl extends FrameLayout implements Dumpable { updateClippingPath(); } + public NonInterceptingScrollView getQSPanelContainer() { + return mQSPanelContainer; + } + public void disable(int state1, int state2, boolean animate) { final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0; if (disabled == mQsDisabled) return; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java index 7d61991c910a..dea7bb5abd71 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java @@ -16,8 +16,13 @@ package com.android.systemui.qs; +import static com.android.systemui.classifier.Classifier.QS_SWIPE_NESTED; + import android.content.res.Configuration; +import android.view.MotionEvent; +import android.view.View; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.ViewController; @@ -30,6 +35,8 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> { private final QSPanelController mQsPanelController; private final QuickStatusBarHeaderController mQuickStatusBarHeaderController; private final ConfigurationController mConfigurationController; + private final FalsingManager mFalsingManager; + private final NonInterceptingScrollView mQSPanelContainer; private final ConfigurationController.ConfigurationListener mConfigurationListener = new ConfigurationController.ConfigurationListener() { @@ -39,14 +46,32 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> { } }; + private final View.OnTouchListener mContainerTouchHandler = new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_UP) { + if (mQSPanelContainer.isPreventingIntercept()) { + // There's really no action here to take, but we need to tell the FalsingManager + mFalsingManager.isFalseTouch(QS_SWIPE_NESTED); + } + } + return false; + } + }; + @Inject - QSContainerImplController(QSContainerImpl view, QSPanelController qsPanelController, + QSContainerImplController( + QSContainerImpl view, + QSPanelController qsPanelController, QuickStatusBarHeaderController quickStatusBarHeaderController, - ConfigurationController configurationController) { + ConfigurationController configurationController, + FalsingManager falsingManager) { super(view); mQsPanelController = qsPanelController; mQuickStatusBarHeaderController = quickStatusBarHeaderController; mConfigurationController = configurationController; + mFalsingManager = falsingManager; + mQSPanelContainer = mView.getQSPanelContainer(); } @Override @@ -62,11 +87,13 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> { protected void onViewAttached() { mView.updateResources(mQsPanelController, mQuickStatusBarHeaderController); mConfigurationController.addCallback(mConfigurationListener); + mQSPanelContainer.setOnTouchListener(mContainerTouchHandler); } @Override protected void onViewDetached() { mConfigurationController.removeCallback(mConfigurationListener); + mQSPanelContainer.setOnTouchListener(null); } public QSContainerImpl getView() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 448e1807f7af..184089f7eef4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -473,6 +473,8 @@ public class QSPanel extends LinearLayout implements Tunable { ? Math.max(mMediaTotalBottomMargin - getPaddingBottom(), 0) : 0; layoutParams.topMargin = mediaNeedsTopMargin() && !horizontal ? mMediaTopMargin : 0; + // Call setLayoutParams explicitly to ensure that requestLayout happens + hostView.setLayoutParams(layoutParams); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java index 59b871cc41bb..f41b905775e4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java @@ -16,12 +16,11 @@ package com.android.systemui.qs; -import static com.android.systemui.classifier.Classifier.QS_SWIPE; +import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE; import static com.android.systemui.media.dagger.MediaModule.QS_PANEL; import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS; import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER; -import android.content.res.Configuration; import android.view.MotionEvent; import android.view.View; @@ -61,22 +60,11 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { private final BrightnessMirrorHandler mBrightnessMirrorHandler; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener = - new QSPanel.OnConfigurationChangedListener() { - @Override - public void onConfigurationChange(Configuration newConfig) { - mView.updateResources(); - if (mView.isListening()) { - refreshAllTiles(); - } - } - }; - private View.OnTouchListener mTileLayoutTouchListener = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_UP) { - mFalsingManager.isFalseTouch(QS_SWIPE); + mFalsingManager.isFalseTouch(QS_SWIPE_SIDE); } return false; } @@ -130,7 +118,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { if (mView.isListening()) { refreshAllTiles(); } - mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener); switchTileLayout(true); mBrightnessMirrorHandler.onQsPanelAttached(); @@ -147,11 +134,18 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { @Override protected void onViewDetached() { mTunerService.removeTunable(mView); - mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener); mBrightnessMirrorHandler.onQsPanelDettached(); super.onViewDetached(); } + @Override + protected void onConfigurationChanged() { + mView.updateResources(); + if (mView.isListening()) { + refreshAllTiles(); + } + } + /** */ public void setVisibility(int visibility) { mView.setVisibility(visibility); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 6e4c858e89df..a5c60a417a05 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -101,11 +101,11 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr + newConfig.windowConfiguration); mQSLogger.logOnConfigurationChanged(mLastOrientation, newConfig.orientation, mView.getDumpableTag()); - onConfigurationChanged(); if (newConfig.orientation != mLastOrientation) { mLastOrientation = newConfig.orientation; switchTileLayout(false); } + onConfigurationChanged(); } }; @@ -422,6 +422,8 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr } if (mMediaHost != null) { pw.println(" media bounds: " + mMediaHost.getCurrentBounds()); + pw.println(" horizontal layout: " + mUsingHorizontalLayout); + pw.println(" last orientation: " + mLastOrientation); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java index c86e6e855c3d..7ce0ad04bb75 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java @@ -46,14 +46,6 @@ import javax.inject.Provider; @QSScope public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> { - private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener = - newConfig -> { - int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_tiles); - if (newMaxTiles != mView.getNumQuickTiles()) { - setMaxTiles(newMaxTiles); - } - }; - private final Provider<Boolean> mUsingCollapsedLandscapeMediaProvider; @Inject @@ -99,13 +91,11 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> @Override protected void onViewAttached() { super.onViewAttached(); - mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener); } @Override protected void onViewDetached() { super.onViewDetached(); - mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener); } private void setMaxTiles(int parseNumTiles) { @@ -115,6 +105,10 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> @Override protected void onConfigurationChanged() { + int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_tiles); + if (newMaxTiles != mView.getNumQuickTiles()) { + setMaxTiles(newMaxTiles); + } updateMediaExpansion(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java index 0ec4eef1e551..97476b2d1cde 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java @@ -16,9 +16,6 @@ package com.android.systemui.qs.tiles; -import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA; -import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA; - import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; @@ -42,6 +39,7 @@ import com.android.systemui.qs.PseudoGridView; import com.android.systemui.qs.QSUserSwitcherEvent; import com.android.systemui.qs.user.UserSwitchDialogController; import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.user.data.source.UserRecord; @@ -73,7 +71,8 @@ public class UserDetailView extends PseudoGridView { mAdapter.refresh(); } - public static class Adapter extends UserSwitcherController.BaseUserAdapter + /** Provides views for user detail items. */ + public static class Adapter extends BaseUserSwitcherAdapter implements OnClickListener { private final Context mContext; @@ -137,7 +136,7 @@ public class UserDetailView extends PseudoGridView { v.setActivated(item.isCurrent); v.setDisabledByAdmin(mController.isDisabledByAdmin(item)); v.setEnabled(item.isSwitchToEnabled); - v.setAlpha(v.isEnabled() ? USER_SWITCH_ENABLED_ALPHA : USER_SWITCH_DISABLED_ALPHA); + UserSwitcherController.setSelectableAlpha(v); if (item.isCurrent) { mCurrentUserView = v; diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 3788ad98092f..7e2a5c51786d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -27,6 +27,8 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION; +import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_DESKTOP_MODE; +import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_FLOATING_TASKS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS; @@ -109,6 +111,8 @@ import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.CallbackController; import com.android.wm.shell.back.BackAnimation; +import com.android.wm.shell.desktopmode.DesktopMode; +import com.android.wm.shell.floating.FloatingTasks; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; @@ -150,6 +154,7 @@ public class OverviewProxyService extends CurrentUserTracker implements private final Optional<Pip> mPipOptional; private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy; private final Optional<SplitScreen> mSplitScreenOptional; + private final Optional<FloatingTasks> mFloatingTasksOptional; private SysUiState mSysUiState; private final Handler mHandler; private final Lazy<NavigationBarController> mNavBarControllerLazy; @@ -166,6 +171,7 @@ public class OverviewProxyService extends CurrentUserTracker implements private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController; private final Optional<RecentTasks> mRecentTasks; private final Optional<BackAnimation> mBackAnimation; + private final Optional<DesktopMode> mDesktopModeOptional; private final UiEventLogger mUiEventLogger; private Region mActiveNavBarRegion; @@ -382,7 +388,6 @@ public class OverviewProxyService extends CurrentUserTracker implements mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::togglePanel)); } - private boolean verifyCaller(String reason) { final int callerId = Binder.getCallingUserHandle().getIdentifier(); if (callerId != mCurrentBoundedUserId) { @@ -466,6 +471,9 @@ public class OverviewProxyService extends CurrentUserTracker implements mSplitScreenOptional.ifPresent((splitscreen) -> params.putBinder( KEY_EXTRA_SHELL_SPLIT_SCREEN, splitscreen.createExternalInterface().asBinder())); + mFloatingTasksOptional.ifPresent(floatingTasks -> params.putBinder( + KEY_EXTRA_SHELL_FLOATING_TASKS, + floatingTasks.createExternalInterface().asBinder())); mOneHandedOptional.ifPresent((onehanded) -> params.putBinder( KEY_EXTRA_SHELL_ONE_HANDED, onehanded.createExternalInterface().asBinder())); @@ -483,6 +491,9 @@ public class OverviewProxyService extends CurrentUserTracker implements mBackAnimation.ifPresent((backAnimation) -> params.putBinder( KEY_EXTRA_SHELL_BACK_ANIMATION, backAnimation.createExternalInterface().asBinder())); + mDesktopModeOptional.ifPresent((desktopMode -> params.putBinder( + KEY_EXTRA_SHELL_DESKTOP_MODE, + desktopMode.createExternalInterface().asBinder()))); try { Log.d(TAG_OPS, "OverviewProxyService connected, initializing overview proxy"); @@ -563,10 +574,12 @@ public class OverviewProxyService extends CurrentUserTracker implements NotificationShadeWindowController statusBarWinController, SysUiState sysUiState, Optional<Pip> pipOptional, Optional<SplitScreen> splitScreenOptional, + Optional<FloatingTasks> floatingTasksOptional, Optional<OneHanded> oneHandedOptional, Optional<RecentTasks> recentTasks, Optional<BackAnimation> backAnimation, Optional<StartingSurface> startingSurface, + Optional<DesktopMode> desktopModeOptional, BroadcastDispatcher broadcastDispatcher, ShellTransitions shellTransitions, ScreenLifecycle screenLifecycle, @@ -601,6 +614,7 @@ public class OverviewProxyService extends CurrentUserTracker implements mShellTransitions = shellTransitions; mRecentTasks = recentTasks; mBackAnimation = backAnimation; + mDesktopModeOptional = desktopModeOptional; mUiEventLogger = uiEventLogger; dumpManager.registerDumpable(getClass().getSimpleName(), this); @@ -631,6 +645,7 @@ public class OverviewProxyService extends CurrentUserTracker implements mCommandQueue = commandQueue; mSplitScreenOptional = splitScreenOptional; + mFloatingTasksOptional = floatingTasksOptional; // Listen for user setup startTracking(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 69ee8e8fb8dc..3fee232b3465 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -555,6 +555,8 @@ public class ScreenshotController { mScreenshotView.announceForAccessibility( mContext.getResources().getString(R.string.screenshot_saving_title))); + mScreenshotView.reset(); + if (mScreenshotView.isAttachedToWindow()) { // if we didn't already dismiss for another reason if (!mScreenshotView.isDismissing()) { @@ -564,7 +566,6 @@ public class ScreenshotController { Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. " + "(dismissing=" + mScreenshotView.isDismissing() + ")"); } - mScreenshotView.reset(); } mPackageName = topComponent == null ? "" : topComponent.getPackageName(); mScreenshotView.setPackageName(mPackageName); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index 5e7fc6faef1f..360fc879731c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -1006,6 +1006,7 @@ public class ScreenshotView extends FrameLayout implements // Clear any references to the bitmap mScreenshotPreview.setImageDrawable(null); mScreenshotPreview.setVisibility(View.INVISIBLE); + mScreenshotPreview.setAlpha(1f); mScreenshotPreviewBorder.setAlpha(0); mPendingSharedTransition = false; mActionsContainerBackground.setVisibility(View.GONE); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 1011a6d831e6..d7e86b6e2919 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -2230,7 +2230,8 @@ public final class NotificationPanelViewController extends PanelViewController { if (cancel) { collapse(false /* delayed */, 1.0f /* speedUpFactor */); } else { - maybeVibrateOnOpening(); + // Window never will receive touch events that typically trigger haptic on open. + maybeVibrateOnOpening(false /* openingWithTouch */); fling(velocity > 1f ? 1000f * velocity : 0, true /* expand */); } onTrackingStopped(false); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 8d74a09ab8b1..6be9bbbf4e0d 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -31,10 +31,15 @@ import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AuthKeyguardMessageArea; import com.android.keyguard.LockIconViewController; +import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.R; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dock.DockManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder; +import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationShadeDepthController; @@ -108,7 +113,10 @@ public class NotificationShadeWindowViewController { NotificationShadeWindowController controller, KeyguardUnlockAnimationController keyguardUnlockAnimationController, AmbientState ambientState, - PulsingGestureListener pulsingGestureListener + PulsingGestureListener pulsingGestureListener, + FeatureFlags featureFlags, + KeyguardBouncerViewModel keyguardBouncerViewModel, + KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory ) { mLockscreenShadeTransitionController = transitionController; mFalsingCollector = falsingCollector; @@ -130,6 +138,12 @@ public class NotificationShadeWindowViewController { // This view is not part of the newly inflated expanded status bar. mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container); + if (featureFlags.isEnabled(Flags.MODERN_BOUNCER)) { + KeyguardBouncerViewBinder.bind( + mView.findViewById(R.id.keyguard_bouncer_container), + keyguardBouncerViewModel, + keyguardBouncerComponentFactory); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java index c3f1e571ab87..b4ce95c434fc 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java @@ -96,6 +96,7 @@ public abstract class PanelViewController { private float mMinExpandHeight; private boolean mPanelUpdateWhenAnimatorEnds; private final boolean mVibrateOnOpening; + private boolean mHasVibratedOnOpen = false; protected boolean mIsLaunchAnimationRunning; private int mFixedDuration = NO_FIXED_DURATION; protected float mOverExpansion; @@ -353,8 +354,8 @@ public abstract class PanelViewController { private void startOpening(MotionEvent event) { updatePanelExpansionAndVisibility(); - maybeVibrateOnOpening(); - + // Reset at start so haptic can be triggered as soon as panel starts to open. + mHasVibratedOnOpen = false; //TODO: keyguard opens QS a different way; log that too? // Log the position of the swipe that opened the panel @@ -368,9 +369,18 @@ public abstract class PanelViewController { .log(LockscreenUiEvent.LOCKSCREEN_UNLOCKED_NOTIFICATION_PANEL_EXPAND); } - protected void maybeVibrateOnOpening() { + /** + * Maybe vibrate as panel is opened. + * + * @param openingWithTouch Whether the panel is being opened with touch. If the panel is instead + * being opened programmatically (such as by the open panel gesture), we always play haptic. + */ + protected void maybeVibrateOnOpening(boolean openingWithTouch) { if (mVibrateOnOpening) { - mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); + if (!openingWithTouch || !mHasVibratedOnOpen) { + mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); + mHasVibratedOnOpen = true; + } } } @@ -1371,6 +1381,9 @@ public abstract class PanelViewController { break; case MotionEvent.ACTION_MOVE: addMovement(event); + if (!isFullyCollapsed()) { + maybeVibrateOnOpening(true /* openingWithTouch */); + } float h = y - mInitialExpandY; // If the panel was collapsed when touching, we only need to check for the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index e06c97756ea7..073ab8b16864 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -1199,7 +1199,8 @@ public class KeyguardIndicationController { return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */) && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED - || msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED); + || msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED + || msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED); } private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 41c0367772b5..d67f94f11e65 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -168,6 +168,17 @@ public class NotificationShelf extends ActivatableNotificationView implements return new ShelfState(); } + @Override + public String toString() { + return "NotificationShelf(" + + "hideBackground=" + mHideBackground + " notGoneIndex=" + mNotGoneIndex + + " hasItemsInStableShelf=" + mHasItemsInStableShelf + + " statusBarState=" + mStatusBarState + " interactive=" + mInteractive + + " animationsEnabled=" + mAnimationsEnabled + + " showNotificationShelf=" + mShowNotificationShelf + + " indexOfFirstViewInShelf=" + mIndexOfFirstViewInShelf + ')'; + } + /** Update the state of the shelf. */ public void updateState(StackScrollAlgorithm.StackScrollAlgorithmState algorithmState, AmbientState ambientState) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 039a3625c70c..827d0d0f8444 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -36,7 +36,6 @@ import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; -import android.os.Parcelable; import android.os.Trace; import android.os.UserHandle; import android.service.notification.StatusBarNotification; @@ -551,9 +550,12 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi } } + @Override public String toString() { - return "StatusBarIconView(slot=" + mSlot + " icon=" + mIcon - + " notification=" + mNotification + ")"; + return "StatusBarIconView(" + + "slot='" + mSlot + " alpha=" + getAlpha() + " icon=" + mIcon + + " iconColor=#" + Integer.toHexString(mIconColor) + + " notification=" + mNotification + ')'; } public StatusBarNotification getNotification() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java deleted file mode 100644 index 4551807499ab..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar; - -import android.content.Context; -import android.content.DialogInterface; - -import com.android.systemui.R; -import com.android.systemui.statusbar.phone.SystemUIDialog; -import com.android.systemui.statusbar.policy.UserSwitcherController; - -public class UserUtil { - public static void deleteUserWithPrompt(Context context, int userId, - UserSwitcherController userSwitcherController) { - new RemoveUserDialog(context, userId, userSwitcherController).show(); - } - - private final static class RemoveUserDialog extends SystemUIDialog implements - DialogInterface.OnClickListener { - - private final int mUserId; - private final UserSwitcherController mUserSwitcherController; - - public RemoveUserDialog(Context context, int userId, - UserSwitcherController userSwitcherController) { - super(context); - setTitle(R.string.user_remove_user_title); - setMessage(context.getString(R.string.user_remove_user_message)); - setButton(DialogInterface.BUTTON_NEUTRAL, - context.getString(android.R.string.cancel), this); - setButton(DialogInterface.BUTTON_POSITIVE, - context.getString(R.string.user_remove_user_remove), this); - setCanceledOnTouchOutside(false); - mUserId = userId; - mUserSwitcherController = userSwitcherController; - } - - @Override - public void onClick(DialogInterface dialog, int which) { - if (which == BUTTON_NEUTRAL) { - cancel(); - } else { - dismiss(); - mUserSwitcherController.removeUserId(mUserId); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index d380e9f03720..d61c51e76e86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -56,7 +56,9 @@ import javax.inject.Inject; /** * A class which manages the bouncer on the lockscreen. + * @deprecated Use KeyguardBouncerRepository */ +@Deprecated public class KeyguardBouncer { private static final String TAG = "KeyguardBouncer"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java index 4d6168989691..00c3e8fac0b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java @@ -33,6 +33,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.qs.FooterActionsView; import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.qs.user.UserSwitchDialogController; +import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.user.UserSwitcherActivity; import com.android.systemui.util.ViewController; @@ -49,7 +50,7 @@ public class MultiUserSwitchController extends ViewController<MultiUserSwitch> { private final ActivityStarter mActivityStarter; private final FeatureFlags mFeatureFlags; - private UserSwitcherController.BaseUserAdapter mUserListener; + private BaseUserSwitcherAdapter mUserListener; private final View.OnClickListener mOnClickListener = new View.OnClickListener() { @Override @@ -135,7 +136,7 @@ public class MultiUserSwitchController extends ViewController<MultiUserSwitch> { final UserSwitcherController controller = mUserSwitcherController; if (controller != null) { - mUserListener = new UserSwitcherController.BaseUserAdapter(controller) { + mUserListener = new BaseUserSwitcherAdapter(controller) { @Override public void notifyDataSetChanged() { mView.refreshContentDescription(getCurrentUser()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 7b8c5fc998fa..5a70d89908f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -278,6 +278,15 @@ public class NotificationIconContainer extends ViewGroup { } } + @Override + public String toString() { + return "NotificationIconContainer(" + + "dozing=" + mDozing + " onLockScreen=" + mOnLockScreen + + " inNotificationIconShelf=" + mInNotificationIconShelf + + " speedBumpIndex=" + mSpeedBumpIndex + + " themedTextColorPrimary=#" + Integer.toHexString(mThemedTextColorPrimary) + ')'; + } + @VisibleForTesting public void setIconSize(int size) { mIconSize = size; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 786714716596..e61794b0243e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -46,6 +46,7 @@ import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.AuthKeyguardMessageArea; import com.android.keyguard.KeyguardMessageAreaController; +import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.KeyguardViewController; @@ -53,6 +54,12 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; +import com.android.systemui.keyguard.data.BouncerView; +import com.android.systemui.keyguard.data.BouncerViewDelegate; +import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor; +import com.android.systemui.keyguard.domain.interactor.BouncerInteractor; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -123,6 +130,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Nullable private final FoldAodAnimationController mFoldAodAnimationController; private KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController; + private final BouncerCallbackInteractor mBouncerCallbackInteractor; + private final BouncerInteractor mBouncerInteractor; + private final BouncerViewDelegate mBouncerViewDelegate; private final Lazy<com.android.systemui.shade.ShadeController> mShadeController; private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() { @@ -197,7 +207,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private View mNotificationContainer; - protected KeyguardBouncer mBouncer; + @Nullable protected KeyguardBouncer mBouncer; protected boolean mShowing; protected boolean mOccluded; protected boolean mRemoteInputActive; @@ -223,6 +233,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private int mLastBiometricMode; private boolean mLastScreenOffAnimationPlaying; private float mQsExpansion; + private boolean mIsModernBouncerEnabled; private OnDismissAction mAfterKeyguardGoneAction; private Runnable mKeyguardGoneCancelAction; @@ -237,6 +248,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final DockManager mDockManager; private final KeyguardUpdateMonitor mKeyguardUpdateManager; private final LatencyTracker mLatencyTracker; + private final KeyguardSecurityModel mKeyguardSecurityModel; private KeyguardBypassController mBypassController; @Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor; @@ -271,7 +283,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb KeyguardMessageAreaController.Factory keyguardMessageAreaFactory, Optional<SysUIUnfoldComponent> sysUIUnfoldComponent, Lazy<ShadeController> shadeController, - LatencyTracker latencyTracker) { + LatencyTracker latencyTracker, + KeyguardSecurityModel keyguardSecurityModel, + FeatureFlags featureFlags, + BouncerCallbackInteractor bouncerCallbackInteractor, + BouncerInteractor bouncerInteractor, + BouncerView bouncerView) { mContext = context; mViewMediatorCallback = callback; mLockPatternUtils = lockPatternUtils; @@ -288,8 +305,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mKeyguardMessageAreaFactory = keyguardMessageAreaFactory; mShadeController = shadeController; mLatencyTracker = latencyTracker; + mKeyguardSecurityModel = keyguardSecurityModel; + mBouncerCallbackInteractor = bouncerCallbackInteractor; + mBouncerInteractor = bouncerInteractor; + mBouncerViewDelegate = bouncerView.getDelegate(); mFoldAodAnimationController = sysUIUnfoldComponent .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null); + mIsModernBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_BOUNCER); } @Override @@ -303,7 +325,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBiometricUnlockController = biometricUnlockController; ViewGroup container = mCentralSurfaces.getBouncerContainer(); - mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback); + if (mIsModernBouncerEnabled) { + mBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback); + } else { + mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback); + } mNotificationPanelViewController = notificationPanelViewController; if (panelExpansionStateManager != null) { panelExpansionStateManager.addExpansionListener(this); @@ -377,29 +403,45 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mDozing && !mPulsing) { return; } else if (mNotificationPanelViewController.isUnlockHintRunning()) { - mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); + if (mBouncer != null) { + mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); + } + mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) { // Don't expand to the bouncer. Instead transition back to the lock screen (see // CentralSurfaces#showBouncerOrLockScreenIfKeyguard) return; } else if (bouncerNeedsScrimming()) { - mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); + if (mBouncer != null) { + mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); + } + mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); } else if (mShowing && !hideBouncerOverDream) { if (!isWakeAndUnlocking() && !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER) && !mCentralSurfaces.isInLaunchTransition() && !isUnlockCollapsing()) { - mBouncer.setExpansion(fraction); + if (mBouncer != null) { + mBouncer.setExpansion(fraction); + } + mBouncerInteractor.setExpansion(fraction); } if (fraction != KeyguardBouncer.EXPANSION_HIDDEN && tracking && !mKeyguardStateController.canDismissLockScreen() - && !mBouncer.isShowing() && !mBouncer.isAnimatingAway()) { - mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */); + && !bouncerIsShowing() + && !bouncerIsAnimatingAway()) { + if (mBouncer != null) { + mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */); + } + mBouncerInteractor.show(/* isScrimmed= */false); } - } else if (!mShowing && mBouncer.inTransit()) { + } else if (!mShowing && isBouncerInTransit()) { // Keyguard is not visible anymore, but expansion animation was still running. // We need to hide the bouncer, otherwise it will be stuck in transit. - mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); + if (mBouncer != null) { + mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); + } + mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); } else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) { // Panel expanded while pulsing but didn't translate the bouncer (because we are // unlocked.) Let's simply wake-up to dismiss the lock screen. @@ -440,15 +482,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * {@link KeyguardBouncer#needsFullscreenBouncer()}. */ protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) { - if (mBouncer.needsFullscreenBouncer() && !mDozing) { + if (needsFullscreenBouncer() && !mDozing) { // The keyguard might be showing (already). So we need to hide it. mCentralSurfaces.hideKeyguard(); - mBouncer.show(true /* resetSecuritySelection */); + if (mBouncer != null) { + mBouncer.show(true /* resetSecuritySelection */); + } + mBouncerInteractor.show(true); } else { mCentralSurfaces.showKeyguard(); if (hideBouncerWhenShowing) { hideBouncer(false /* destroyView */); - mBouncer.prepare(); + if (mBouncer != null) { + mBouncer.prepare(); + } } } updateStates(); @@ -480,10 +527,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb */ @VisibleForTesting void hideBouncer(boolean destroyView) { - if (mBouncer == null) { - return; + if (mBouncer != null) { + mBouncer.hide(destroyView); } - mBouncer.hide(destroyView); + mBouncerInteractor.hide(); if (mShowing) { // If we were showing the bouncer and then aborting, we need to also clear out any // potential actions unless we actually unlocked. @@ -501,8 +548,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public void showBouncer(boolean scrimmed) { resetAlternateAuth(false); - if (mShowing && !mBouncer.isShowing()) { - mBouncer.show(false /* resetSecuritySelection */, scrimmed); + if (mShowing && !isBouncerShowing()) { + if (mBouncer != null) { + mBouncer.show(false /* resetSecuritySelection */, scrimmed); + } + mBouncerInteractor.show(scrimmed); } updateStates(); } @@ -535,7 +585,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // instead of the bouncer. if (shouldShowAltAuth()) { if (!afterKeyguardGone) { - mBouncer.setDismissAction(mAfterKeyguardGoneAction, + if (mBouncer != null) { + mBouncer.setDismissAction(mAfterKeyguardGoneAction, + mKeyguardGoneCancelAction); + } + mBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction, mKeyguardGoneCancelAction); mAfterKeyguardGoneAction = null; mKeyguardGoneCancelAction = null; @@ -549,12 +603,18 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (afterKeyguardGone) { // we'll handle the dismiss action after keyguard is gone, so just show the // bouncer - mBouncer.show(false /* resetSecuritySelection */); + mBouncerInteractor.show(/* isScrimmed= */true); + if (mBouncer != null) mBouncer.show(false /* resetSecuritySelection */); } else { // after authentication success, run dismiss action with the option to defer // hiding the keyguard based on the return value of the OnDismissAction - mBouncer.showWithDismissAction(mAfterKeyguardGoneAction, - mKeyguardGoneCancelAction); + mBouncerInteractor.setDismissAction( + mAfterKeyguardGoneAction, mKeyguardGoneCancelAction); + mBouncerInteractor.show(/* isScrimmed= */true); + if (mBouncer != null) { + mBouncer.showWithDismissAction(mAfterKeyguardGoneAction, + mKeyguardGoneCancelAction); + } // bouncer will handle the dismiss action, so we no longer need to track it here mAfterKeyguardGoneAction = null; mKeyguardGoneCancelAction = null; @@ -591,7 +651,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // Hide bouncer and quick-quick settings. if (mOccluded && !mDozing) { mCentralSurfaces.hideKeyguard(); - if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) { + if (hideBouncerWhenShowing || needsFullscreenBouncer()) { hideBouncer(false /* destroyView */); } } else { @@ -655,7 +715,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void onFinishedGoingToSleep() { - mBouncer.onScreenTurnedOff(); + if (mBouncer != null) { + mBouncer.onScreenTurnedOff(); + } + mBouncerInteractor.onScreenTurnedOff(); } @Override @@ -746,7 +809,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // by a FLAG_DISMISS_KEYGUARD_ACTIVITY. reset(isOccluding /* hideBouncerWhenShowing*/); } - if (animate && !mOccluded && mShowing && !mBouncer.isShowing()) { + if (animate && !mOccluded && mShowing && !bouncerIsShowing()) { mCentralSurfaces.animateKeyguardUnoccluding(); } } @@ -762,8 +825,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void startPreHideAnimation(Runnable finishRunnable) { - if (mBouncer.isShowing()) { - mBouncer.startPreHideAnimation(finishRunnable); + if (bouncerIsShowing()) { + if (mBouncer != null) { + mBouncer.startPreHideAnimation(finishRunnable); + } + mBouncerInteractor.startDisappearAnimation(finishRunnable); mCentralSurfaces.onBouncerPreHideAnimation(); // We update the state (which will show the keyguard) only if an animation will run on @@ -873,8 +939,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } public void onThemeChanged() { - boolean wasShowing = mBouncer.isShowing(); - boolean wasScrimmed = mBouncer.isScrimmed(); + if (mIsModernBouncerEnabled) { + updateResources(); + return; + } + boolean wasShowing = bouncerIsShowing(); + boolean wasScrimmed = bouncerIsScrimmed(); hideBouncer(true /* destroyView */); mBouncer.prepare(); @@ -924,7 +994,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * WARNING: This method might cause Binder calls. */ public boolean isSecure() { - return mBouncer.isSecure(); + if (mBouncer != null) { + return mBouncer.isSecure(); + } + + return mKeyguardSecurityModel.getSecurityMode( + KeyguardUpdateMonitor.getCurrentUser()) != KeyguardSecurityModel.SecurityMode.None; } @Override @@ -941,10 +1016,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * @return whether the back press has been handled */ public boolean onBackPressed(boolean hideImmediately) { - if (mBouncer.isShowing()) { + if (bouncerIsShowing()) { mCentralSurfaces.endAffordanceLaunch(); // The second condition is for SIM card locked bouncer - if (mBouncer.isScrimmed() && !mBouncer.needsFullscreenBouncer()) { + if (bouncerIsScrimmed() + && !needsFullscreenBouncer()) { hideBouncer(false); updateStates(); } else { @@ -957,16 +1033,19 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public boolean isBouncerShowing() { - return mBouncer.isShowing() || isShowingAlternateAuth(); + return bouncerIsShowing() || isShowingAlternateAuth(); } @Override public boolean bouncerIsOrWillBeShowing() { - return isBouncerShowing() || mBouncer.inTransit(); + return isBouncerShowing() || isBouncerInTransit(); } public boolean isFullscreenBouncer() { - return mBouncer.isFullscreenBouncer(); + if (mBouncerViewDelegate != null) { + return mBouncerViewDelegate.isFullScreenBouncer(); + } + return mBouncer != null && mBouncer.isFullscreenBouncer(); } /** @@ -987,7 +1066,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private long getNavBarShowDelay() { if (mKeyguardStateController.isKeyguardFadingAway()) { return mKeyguardStateController.getKeyguardFadingAwayDelay(); - } else if (mBouncer.isShowing()) { + } else if (isBouncerShowing()) { return NAV_BAR_SHOW_DELAY_BOUNCER; } else { // No longer dozing, or remote input is active. No delay. @@ -1010,18 +1089,24 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb protected void updateStates() { boolean showing = mShowing; boolean occluded = mOccluded; - boolean bouncerShowing = mBouncer.isShowing(); + boolean bouncerShowing = bouncerIsShowing(); boolean bouncerIsOrWillBeShowing = bouncerIsOrWillBeShowing(); - boolean bouncerDismissible = !mBouncer.isFullscreenBouncer(); + boolean bouncerDismissible = !isFullscreenBouncer(); boolean remoteInputActive = mRemoteInputActive; if ((bouncerDismissible || !showing || remoteInputActive) != (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive) || mFirstUpdate) { if (bouncerDismissible || !showing || remoteInputActive) { - mBouncer.setBackButtonEnabled(true); + if (mBouncer != null) { + mBouncer.setBackButtonEnabled(true); + } + mBouncerInteractor.setBackButtonEnabled(true); } else { - mBouncer.setBackButtonEnabled(false); + if (mBouncer != null) { + mBouncer.setBackButtonEnabled(false); + } + mBouncerInteractor.setBackButtonEnabled(false); } } @@ -1098,7 +1183,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb || mPulsing && !mIsDocked) && mGesturalNav; return (!keyguardShowing && !hideWhileDozing && !mScreenOffAnimationPlaying - || mBouncer.isShowing() || mRemoteInputActive || keyguardWithGestureNav + || bouncerIsShowing() + || mRemoteInputActive + || keyguardWithGestureNav || mGlobalActionsVisible); } @@ -1117,18 +1204,27 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } public boolean shouldDismissOnMenuPressed() { - return mBouncer.shouldDismissOnMenuPressed(); + if (mBouncerViewDelegate != null) { + return mBouncerViewDelegate.shouldDismissOnMenuPressed(); + } + return mBouncer != null && mBouncer.shouldDismissOnMenuPressed(); } public boolean interceptMediaKey(KeyEvent event) { - return mBouncer.interceptMediaKey(event); + if (mBouncerViewDelegate != null) { + return mBouncerViewDelegate.interceptMediaKey(event); + } + return mBouncer != null && mBouncer.interceptMediaKey(event); } /** * @return true if the pre IME back event should be handled */ public boolean dispatchBackKeyEventPreIme() { - return mBouncer.dispatchBackKeyEventPreIme(); + if (mBouncerViewDelegate != null) { + return mBouncerViewDelegate.dispatchBackKeyEventPreIme(); + } + return mBouncer != null && mBouncer.dispatchBackKeyEventPreIme(); } public void readyForKeyguardDone() { @@ -1151,7 +1247,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } public boolean isSecure(int userId) { - return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId); + return isSecure() || mLockPatternUtils.isSecure(userId); } @Override @@ -1174,7 +1270,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * fingerprint. */ public void notifyKeyguardAuthenticated(boolean strongAuth) { - mBouncer.notifyKeyguardAuthenticated(strongAuth); + if (mBouncer != null) { + mBouncer.notifyKeyguardAuthenticated(strongAuth); + } + mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth); if (mAlternateAuthInterceptor != null && isShowingAlternateAuthOrAnimating()) { resetAlternateAuth(false); @@ -1189,7 +1288,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mKeyguardMessageAreaController.setMessage(message); } } else { - mBouncer.showMessage(message, colorState); + if (mBouncer != null) { + mBouncer.showMessage(message, colorState); + } + mBouncerInteractor.showMessage(message, colorState); } } @@ -1222,9 +1324,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public boolean bouncerNeedsScrimming() { // When a dream overlay is active, scrimming will cause any expansion to immediately expand. return (mOccluded && !mDreamOverlayStateController.isOverlayActive()) - || mBouncer.willDismissWithAction() - || (mBouncer.isShowing() && mBouncer.isScrimmed()) - || mBouncer.isFullscreenBouncer(); + || bouncerWillDismissWithAction() + || (bouncerIsShowing() + && bouncerIsScrimmed()) + || isFullscreenBouncer(); } /** @@ -1236,6 +1339,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mBouncer != null) { mBouncer.updateResources(); } + mBouncerInteractor.updateResources(); } public void dump(PrintWriter pw) { @@ -1289,6 +1393,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } } + @Nullable public KeyguardBouncer getBouncer() { return mBouncer; } @@ -1320,6 +1425,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mBouncer != null) { mBouncer.updateKeyguardPosition(x); } + + mBouncerInteractor.setKeyguardPosition(x); } private static class DismissWithActionRequest { @@ -1359,9 +1466,65 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * Returns if bouncer expansion is between 0 and 1 non-inclusive. */ public boolean isBouncerInTransit() { - if (mBouncer == null) return false; + if (mBouncer != null) { + return mBouncer.inTransit(); + } + + return mBouncerInteractor.isInTransit(); + } + + /** + * Returns if bouncer is showing + */ + public boolean bouncerIsShowing() { + if (mBouncer != null) { + return mBouncer.isShowing(); + } + + return mBouncerInteractor.isFullyShowing(); + } + + /** + * Returns if bouncer is scrimmed + */ + public boolean bouncerIsScrimmed() { + if (mBouncer != null) { + return mBouncer.isScrimmed(); + } + + return mBouncerInteractor.isScrimmed(); + } - return mBouncer.inTransit(); + /** + * Returns if bouncer is animating away + */ + public boolean bouncerIsAnimatingAway() { + if (mBouncer != null) { + return mBouncer.isAnimatingAway(); + } + + return mBouncerInteractor.isAnimatingAway(); + } + + /** + * Returns if bouncer will dismiss with action + */ + public boolean bouncerWillDismissWithAction() { + if (mBouncer != null) { + return mBouncer.willDismissWithAction(); + } + + return mBouncerInteractor.willDismissWithAction(); + } + + /** + * Returns if bouncer needs fullscreen bouncer. i.e. sim pin security method + */ + public boolean needsFullscreenBouncer() { + KeyguardSecurityModel.SecurityMode mode = mKeyguardSecurityModel.getSecurityMode( + KeyguardUpdateMonitor.getCurrentUser()); + return mode == KeyguardSecurityModel.SecurityMode.SimPin + || mode == KeyguardSecurityModel.SecurityMode.SimPuk; } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt new file mode 100644 index 000000000000..5b2d69564585 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.android.systemui.statusbar.policy + +import android.content.Context +import android.graphics.ColorFilter +import android.graphics.ColorMatrix +import android.graphics.ColorMatrixColorFilter +import android.graphics.drawable.Drawable +import android.os.UserHandle +import android.widget.BaseAdapter +import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower +import com.android.systemui.user.data.source.UserRecord +import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper.getUserRecordName +import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper.getUserSwitcherActionIconResourceId +import java.lang.ref.WeakReference + +/** Provides views for user switcher experiences. */ +abstract class BaseUserSwitcherAdapter +protected constructor( + protected val controller: UserSwitcherController, +) : BaseAdapter() { + + protected open val users: ArrayList<UserRecord> + get() = controller.users + + init { + controller.addAdapter(WeakReference(this)) + } + + override fun getCount(): Int { + return if (controller.isKeyguardShowing) { + users.count { !it.isRestricted } + } else { + users.size + } + } + + override fun getItem(position: Int): UserRecord { + return users[position] + } + + override fun getItemId(position: Int): Long { + return position.toLong() + } + + /** + * Notifies that a user item in the UI has been clicked. + * + * If the user switcher is hosted in a dialog, passing a non-null [dialogShower] will allow + * animation to and from the parent dialog. + */ + @JvmOverloads + fun onUserListItemClicked( + record: UserRecord, + dialogShower: DialogShower? = null, + ) { + controller.onUserListItemClicked(record, dialogShower) + } + + open fun getName(context: Context, item: UserRecord): String { + return getName(context, item, false) + } + + /** Returns the name for the given {@link UserRecord}. */ + open fun getName(context: Context, item: UserRecord, isTablet: Boolean): String { + return getUserRecordName( + context = context, + record = item, + isGuestUserAutoCreated = controller.isGuestUserAutoCreated, + isGuestUserResetting = controller.isGuestUserResetting, + isTablet = isTablet, + ) + } + + fun refresh() { + controller.refreshUsers(UserHandle.USER_NULL) + } + + companion object { + @JvmStatic + protected val disabledUserAvatarColorFilter: ColorFilter by lazy { + val matrix = ColorMatrix() + matrix.setSaturation(0f) // 0 - grayscale + ColorMatrixColorFilter(matrix) + } + + @JvmStatic + @JvmOverloads + protected fun getIconDrawable( + context: Context, + item: UserRecord, + isTablet: Boolean = false, + ): Drawable { + val iconRes = + getUserSwitcherActionIconResourceId( + item.isAddUser, + item.isGuest, + item.isAddSupervisedUser, + isTablet, + ) + return checkNotNull(context.getDrawable(iconRes)) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index 753e94015751..149ed0a71b91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -19,14 +19,17 @@ package com.android.systemui.statusbar.policy; import android.annotation.Nullable; import android.view.View; -import com.android.systemui.Dumpable; import com.android.systemui.demomode.DemoMode; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; import java.io.PrintWriter; import java.lang.ref.WeakReference; -public interface BatteryController extends DemoMode, Dumpable, +/** + * Controller for battery related information, including the charge level, power save mode, + * and time remaining information + */ +public interface BatteryController extends DemoMode, CallbackController<BatteryStateChangeCallback> { /** * Prints the current state of the {@link BatteryController} to the given {@link PrintWriter}. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index 33ddf7eed006..c7ad76722929 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -38,11 +38,13 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.fuelgauge.BatterySaverUtils; import com.android.settingslib.fuelgauge.Estimate; import com.android.settingslib.utils.PowerUtil; +import com.android.systemui.Dumpable; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.dump.DumpManager; import com.android.systemui.power.EnhancedEstimates; import com.android.systemui.util.Assert; @@ -56,7 +58,8 @@ import java.util.concurrent.atomic.AtomicReference; * Default implementation of a {@link BatteryController}. This controller monitors for battery * level change events that are broadcasted by the system. */ -public class BatteryControllerImpl extends BroadcastReceiver implements BatteryController { +public class BatteryControllerImpl extends BroadcastReceiver implements BatteryController, + Dumpable { private static final String TAG = "BatteryController"; private static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST"; @@ -70,6 +73,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC private final ArrayList<EstimateFetchCompletion> mFetchCallbacks = new ArrayList<>(); private final PowerManager mPowerManager; private final DemoModeController mDemoModeController; + private final DumpManager mDumpManager; private final Handler mMainHandler; private final Handler mBgHandler; protected final Context mContext; @@ -101,6 +105,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC PowerManager powerManager, BroadcastDispatcher broadcastDispatcher, DemoModeController demoModeController, + DumpManager dumpManager, @Main Handler mainHandler, @Background Handler bgHandler) { mContext = context; @@ -110,6 +115,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC mEstimates = enhancedEstimates; mBroadcastDispatcher = broadcastDispatcher; mDemoModeController = demoModeController; + mDumpManager = dumpManager; } private void registerReceiver() { @@ -134,6 +140,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } } mDemoModeController.addCallback(this); + mDumpManager.registerDumpable(TAG, this); updatePowerSave(); updateEstimateInBackground(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java index 163060814545..dc73d1f007c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java @@ -69,7 +69,7 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout> private final Context mContext; private Resources mResources; private final UserSwitcherController mUserSwitcherController; - private UserSwitcherController.BaseUserAdapter mAdapter; + private BaseUserSwitcherAdapter mAdapter; private final KeyguardStateController mKeyguardStateController; private final FalsingManager mFalsingManager; protected final SysuiStatusBarStateController mStatusBarStateController; @@ -171,7 +171,7 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout> mUserAvatarView = mView.findViewById(R.id.kg_multi_user_avatar); mUserAvatarViewWithBackground = mView.findViewById( R.id.kg_multi_user_avatar_with_background); - mAdapter = new UserSwitcherController.BaseUserAdapter(mUserSwitcherController) { + mAdapter = new BaseUserSwitcherAdapter(mUserSwitcherController) { @Override public View getView(int position, View convertView, ViewGroup parent) { return null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java index e2f5734cb4f9..0995a00533a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java @@ -16,9 +16,6 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA; -import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -232,14 +229,8 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS } /** - * See: - * - * <ul> - * <li>{@link com.android.internal.R.bool.config_expandLockScreenUserSwitcher}</li> - * <li>{@link UserSwitcherController.SIMPLE_USER_SWITCHER_GLOBAL_SETTING}</li> - * </ul> + * Returns {@code true} if the user switcher should be open by default on the lock screen. * - * @return true if the user switcher should be open by default on the lock screen. * @see android.os.UserManager#isUserSwitcherEnabled() */ public boolean isSimpleUserSwitcher() { @@ -436,7 +427,7 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS } static class KeyguardUserAdapter extends - UserSwitcherController.BaseUserAdapter implements View.OnClickListener { + BaseUserSwitcherAdapter implements View.OnClickListener { private final Context mContext; private final Resources mResources; @@ -514,9 +505,9 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS v.bind(name, drawable, item.info.id); } v.setActivated(item.isCurrent); - v.setDisabledByAdmin(mController.isDisabledByAdmin(item)); + v.setDisabledByAdmin(getController().isDisabledByAdmin(item)); v.setEnabled(item.isSwitchToEnabled); - v.setAlpha(v.isEnabled() ? USER_SWITCH_ENABLED_ALPHA : USER_SWITCH_DISABLED_ALPHA); + UserSwitcherController.setSelectableAlpha(v); if (item.isCurrent) { mCurrentUserView = v; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt new file mode 100644 index 000000000000..843c2329092c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.android.systemui.statusbar.policy + +import android.annotation.UserIdInt +import android.content.Intent +import android.view.View +import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin +import com.android.systemui.Dumpable +import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower +import com.android.systemui.user.data.source.UserRecord +import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper +import java.lang.ref.WeakReference +import kotlinx.coroutines.flow.Flow + +/** Defines interface for a class that provides user switching functionality and state. */ +interface UserSwitcherController : Dumpable { + + /** The current list of [UserRecord]. */ + val users: ArrayList<UserRecord> + + /** Whether the user switcher experience should use the simple experience. */ + val isSimpleUserSwitcher: Boolean + + /** Require a view for jank detection */ + fun init(view: View) + + /** The [UserRecord] of the current user or `null` when none. */ + val currentUserRecord: UserRecord? + + /** The name of the current user of the device or `null`, when none is selected. */ + val currentUserName: String? + + /** + * Notifies that a user has been selected. + * + * This will trigger the right user journeys to create a guest user, switch users, and/or + * navigate to the correct destination. + * + * If a user with the given ID is not found, this method is a no-op. + * + * @param userId The ID of the user to switch to. + * @param dialogShower An optional [DialogShower] in case we need to show dialogs. + */ + fun onUserSelected(userId: Int, dialogShower: DialogShower?) + + /** Whether it is allowed to add users while the device is locked. */ + val isAddUsersFromLockScreenEnabled: Flow<Boolean> + + /** Whether the guest user is configured to always be present on the device. */ + val isGuestUserAutoCreated: Boolean + + /** Whether the guest user is currently being reset. */ + val isGuestUserResetting: Boolean + + /** Creates and switches to the guest user. */ + fun createAndSwitchToGuestUser(dialogShower: DialogShower?) + + /** Shows the add user dialog. */ + fun showAddUserDialog(dialogShower: DialogShower?) + + /** Starts an activity to add a supervised user to the device. */ + fun startSupervisedUserActivity() + + /** Notifies when the display density or font scale has changed. */ + fun onDensityOrFontScaleChanged() + + /** Registers an adapter to notify when the users change. */ + fun addAdapter(adapter: WeakReference<BaseUserSwitcherAdapter>) + + /** Notifies the item for a user has been clicked. */ + fun onUserListItemClicked(record: UserRecord, dialogShower: DialogShower?) + + /** + * Removes guest user and switches to target user. The guest must be the current user and its id + * must be `guestUserId`. + * + * If `targetUserId` is `UserHandle.USER_NULL`, then create a new guest user in the foreground, + * and immediately switch to it. This is used for wiping the current guest and replacing it with + * a new one. + * + * If `targetUserId` is specified, then remove the guest in the background while switching to + * `targetUserId`. + * + * If device is configured with `config_guestUserAutoCreated`, then after guest user is removed, + * a new one is created in the background. This has no effect if `targetUserId` is + * `UserHandle.USER_NULL`. + * + * @param guestUserId id of the guest user to remove + * @param targetUserId id of the user to switch to after guest is removed. If + * `UserHandle.USER_NULL`, then switch immediately to the newly created guest user. + */ + fun removeGuestUser(@UserIdInt guestUserId: Int, @UserIdInt targetUserId: Int) + + /** + * Exits guest user and switches to previous non-guest user. The guest must be the current user. + * + * @param guestUserId user id of the guest user to exit + * @param targetUserId user id of the guest user to exit, set to UserHandle#USER_NULL when + * target user id is not known + * @param forceRemoveGuestOnExit true: remove guest before switching user, false: remove guest + * only if its ephemeral, else keep guest + */ + fun exitGuestUser( + @UserIdInt guestUserId: Int, + @UserIdInt targetUserId: Int, + forceRemoveGuestOnExit: Boolean + ) + + /** + * Guarantee guest is present only if the device is provisioned. Otherwise, create a content + * observer to wait until the device is provisioned, then schedule the guest creation. + */ + fun schedulePostBootGuestCreation() + + /** Whether keyguard is showing. */ + val isKeyguardShowing: Boolean + + /** Returns the [EnforcedAdmin] for the given record, or `null` if there isn't one. */ + fun getEnforcedAdmin(record: UserRecord): EnforcedAdmin? + + /** Returns `true` if the given record is disabled by the admin; `false` otherwise. */ + fun isDisabledByAdmin(record: UserRecord): Boolean + + /** Starts an activity with the given [Intent]. */ + fun startActivity(intent: Intent) + + /** + * Refreshes users from UserManager. + * + * The pictures are only loaded if they have not been loaded yet. + * + * @param forcePictureLoadForId forces the picture of the given user to be reloaded. + */ + fun refreshUsers(forcePictureLoadForId: Int) + + /** Adds a subscriber to when user switches. */ + fun addUserSwitchCallback(callback: UserSwitchCallback) + + /** Removes a previously-added subscriber. */ + fun removeUserSwitchCallback(callback: UserSwitchCallback) + + /** Defines interface for classes that can be called back when the user is switched. */ + fun interface UserSwitchCallback { + /** Notifies that the user has switched. */ + fun onUserSwitched() + } + + companion object { + /** Alpha value to apply to a user view in the user switcher when it's selectable. */ + private const val ENABLED_ALPHA = + LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA + + /** Alpha value to apply to a user view in the user switcher when it's not selectable. */ + private const val DISABLED_ALPHA = + LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA + + @JvmStatic + fun setSelectableAlpha(view: View) { + view.alpha = + if (view.isEnabled) { + ENABLED_ALPHA + } else { + DISABLED_ALPHA + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt new file mode 100644 index 000000000000..12834f68c3b7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.statusbar.policy + +import android.content.Intent +import android.view.View +import com.android.settingslib.RestrictedLockUtils +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.qs.user.UserSwitchDialogController +import com.android.systemui.user.data.source.UserRecord +import dagger.Lazy +import java.io.PrintWriter +import java.lang.ref.WeakReference +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** Implementation of [UserSwitcherController]. */ +class UserSwitcherControllerImpl +@Inject +constructor( + private val flags: FeatureFlags, + @Suppress("DEPRECATION") private val oldImpl: Lazy<UserSwitcherControllerOldImpl>, +) : UserSwitcherController { + + private val isNewImpl: Boolean + get() = flags.isEnabled(Flags.REFACTORED_USER_SWITCHER_CONTROLLER) + private val _oldImpl: UserSwitcherControllerOldImpl + get() = oldImpl.get() + + private fun notYetImplemented(): Nothing { + error("Not yet implemented!") + } + + override val users: ArrayList<UserRecord> + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.users + } + + override val isSimpleUserSwitcher: Boolean + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.isSimpleUserSwitcher + } + + override fun init(view: View) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.init(view) + } + } + + override val currentUserRecord: UserRecord? + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.currentUserRecord + } + + override val currentUserName: String? + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.currentUserName + } + + override fun onUserSelected( + userId: Int, + dialogShower: UserSwitchDialogController.DialogShower? + ) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.onUserSelected(userId, dialogShower) + } + } + + override val isAddUsersFromLockScreenEnabled: Flow<Boolean> + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.isAddUsersFromLockScreenEnabled + } + + override val isGuestUserAutoCreated: Boolean + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.isGuestUserAutoCreated + } + + override val isGuestUserResetting: Boolean + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.isGuestUserResetting + } + + override fun createAndSwitchToGuestUser( + dialogShower: UserSwitchDialogController.DialogShower?, + ) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.createAndSwitchToGuestUser(dialogShower) + } + } + + override fun showAddUserDialog(dialogShower: UserSwitchDialogController.DialogShower?) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.showAddUserDialog(dialogShower) + } + } + + override fun startSupervisedUserActivity() { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.startSupervisedUserActivity() + } + } + + override fun onDensityOrFontScaleChanged() { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.onDensityOrFontScaleChanged() + } + } + + override fun addAdapter(adapter: WeakReference<BaseUserSwitcherAdapter>) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.addAdapter(adapter) + } + } + + override fun onUserListItemClicked( + record: UserRecord, + dialogShower: UserSwitchDialogController.DialogShower?, + ) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.onUserListItemClicked(record, dialogShower) + } + } + + override fun removeGuestUser(guestUserId: Int, targetUserId: Int) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.removeGuestUser(guestUserId, targetUserId) + } + } + + override fun exitGuestUser( + guestUserId: Int, + targetUserId: Int, + forceRemoveGuestOnExit: Boolean + ) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit) + } + } + + override fun schedulePostBootGuestCreation() { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.schedulePostBootGuestCreation() + } + } + + override val isKeyguardShowing: Boolean + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.isKeyguardShowing + } + + override fun getEnforcedAdmin(record: UserRecord): RestrictedLockUtils.EnforcedAdmin? { + return if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.getEnforcedAdmin(record) + } + } + + override fun isDisabledByAdmin(record: UserRecord): Boolean { + return if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.isDisabledByAdmin(record) + } + } + + override fun startActivity(intent: Intent) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.startActivity(intent) + } + } + + override fun refreshUsers(forcePictureLoadForId: Int) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.refreshUsers(forcePictureLoadForId) + } + } + + override fun addUserSwitchCallback(callback: UserSwitcherController.UserSwitchCallback) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.addUserSwitchCallback(callback) + } + } + + override fun removeUserSwitchCallback(callback: UserSwitcherController.UserSwitchCallback) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.removeUserSwitchCallback(callback) + } + } + + override fun dump(pw: PrintWriter, args: Array<out String>) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.dump(pw, args) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java index 1d5b88e7dee0..d365aa6f952d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java @@ -11,9 +11,8 @@ * 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 + * limitations under the License. */ - package com.android.systemui.statusbar.policy; import static android.os.UserManager.SWITCHABILITY_STATUS_OK; @@ -34,10 +33,6 @@ import android.content.IntentFilter; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.graphics.Bitmap; -import android.graphics.ColorFilter; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; @@ -51,7 +46,6 @@ import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.View; import android.view.WindowManagerGlobal; -import android.widget.BaseAdapter; import android.widget.Toast; import androidx.annotation.Nullable; @@ -63,7 +57,6 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.util.LatencyTracker; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.users.UserCreatingDialog; -import com.android.systemui.Dumpable; import com.android.systemui.GuestResetOrExitSessionReceiver; import com.android.systemui.GuestResumeSessionReceiver; import com.android.systemui.R; @@ -86,7 +79,6 @@ import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.user.CreateUserActivity; import com.android.systemui.user.data.source.UserRecord; -import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper; import com.android.systemui.util.settings.GlobalSettings; import com.android.systemui.util.settings.SecureSettings; @@ -106,15 +98,14 @@ import kotlinx.coroutines.flow.MutableStateFlow; import kotlinx.coroutines.flow.StateFlowKt; /** - * Keeps a list of all users on the device for user switching. + * Old implementation. Keeps a list of all users on the device for user switching. + * + * @deprecated This is the old implementation. Please depend on {@link UserSwitcherController} + * instead. */ +@Deprecated @SysUISingleton -public class UserSwitcherController implements Dumpable { - - public static final float USER_SWITCH_ENABLED_ALPHA = - LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA; - public static final float USER_SWITCH_DISABLED_ALPHA = - LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA; +public class UserSwitcherControllerOldImpl implements UserSwitcherController { private static final String TAG = "UserSwitcherController"; private static final boolean DEBUG = false; @@ -123,7 +114,7 @@ public class UserSwitcherController implements Dumpable { private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000; private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; - private static final long MULTI_USER_JOURNEY_TIMEOUT = 20000l; + private static final long MULTI_USER_JOURNEY_TIMEOUT = 20000L; private static final String INTERACTION_JANK_ADD_NEW_USER_TAG = "add_new_user"; private static final String INTERACTION_JANK_EXIT_GUEST_MODE_TAG = "exit_guest_mode"; @@ -132,7 +123,7 @@ public class UserSwitcherController implements Dumpable { protected final UserTracker mUserTracker; protected final UserManager mUserManager; private final ContentObserver mSettingsObserver; - private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>(); + private final ArrayList<WeakReference<BaseUserSwitcherAdapter>> mAdapters = new ArrayList<>(); @VisibleForTesting final GuestResumeSessionReceiver mGuestResumeSessionReceiver; @VisibleForTesting @@ -158,7 +149,6 @@ public class UserSwitcherController implements Dumpable { @VisibleForTesting Dialog mAddUserDialog; private int mLastNonGuestUser = UserHandle.USER_SYSTEM; - private boolean mResumeUserOnGuestLogout = true; private boolean mSimpleUserSwitcher; // When false, there won't be any visual affordance to add a new user from the keyguard even if // the user is unlocked @@ -187,7 +177,8 @@ public class UserSwitcherController implements Dumpable { Collections.synchronizedList(new ArrayList<>()); @Inject - public UserSwitcherController(Context context, + public UserSwitcherControllerOldImpl( + Context context, IActivityManager activityManager, UserManager userManager, UserTracker userTracker, @@ -303,16 +294,10 @@ public class UserSwitcherController implements Dumpable { refreshUsers(UserHandle.USER_NULL); } - /** - * Refreshes users from UserManager. - * - * The pictures are only loaded if they have not been loaded yet. - * - * @param forcePictureLoadForId forces the picture of the given user to be reloaded. - */ + @Override @SuppressWarnings("unchecked") - private void refreshUsers(int forcePictureLoadForId) { - if (DEBUG) Log.d(TAG, "refreshUsers(forcePictureLoadForId=" + forcePictureLoadForId+")"); + public void refreshUsers(int forcePictureLoadForId) { + if (DEBUG) Log.d(TAG, "refreshUsers(forcePictureLoadForId=" + forcePictureLoadForId + ")"); if (forcePictureLoadForId != UserHandle.USER_NULL) { mForcePictureLoadForUserId.put(forcePictureLoadForId, true); } @@ -323,8 +308,8 @@ public class UserSwitcherController implements Dumpable { boolean forceAllUsers = mForcePictureLoadForUserId.get(UserHandle.USER_ALL); SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size()); - final int N = mUsers.size(); - for (int i = 0; i < N; i++) { + final int userCount = mUsers.size(); + for (int i = 0; i < userCount; i++) { UserRecord r = mUsers.get(i); if (r == null || r.picture == null || r.info == null || forceAllUsers || mForcePictureLoadForUserId.get(r.info.id)) { @@ -431,38 +416,41 @@ public class UserSwitcherController implements Dumpable { }); } - boolean systemCanCreateUsers() { + private boolean systemCanCreateUsers() { return !mUserManager.hasBaseUserRestriction( UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM); } - boolean currentUserCanCreateUsers() { + private boolean currentUserCanCreateUsers() { UserInfo currentUser = mUserTracker.getUserInfo(); return currentUser != null && (currentUser.isAdmin() || mUserTracker.getUserId() == UserHandle.USER_SYSTEM) && systemCanCreateUsers(); } - boolean anyoneCanCreateUsers() { + private boolean anyoneCanCreateUsers() { return systemCanCreateUsers() && mAddUsersFromLockScreen.getValue(); } + @VisibleForTesting boolean canCreateGuest(boolean hasExistingGuest) { return mUserSwitcherEnabled && (currentUserCanCreateUsers() || anyoneCanCreateUsers()) && !hasExistingGuest; } + @VisibleForTesting boolean canCreateUser() { return mUserSwitcherEnabled && (currentUserCanCreateUsers() || anyoneCanCreateUsers()) && mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY); } - boolean createIsRestricted() { + private boolean createIsRestricted() { return !mAddUsersFromLockScreen.getValue(); } + @VisibleForTesting boolean canCreateSupervisedUser() { return !TextUtils.isEmpty(mCreateSupervisedUserPackage) && canCreateUser(); } @@ -476,7 +464,7 @@ public class UserSwitcherController implements Dumpable { private void notifyAdapters() { for (int i = mAdapters.size() - 1; i >= 0; i--) { - BaseUserAdapter adapter = mAdapters.get(i).get(); + BaseUserSwitcherAdapter adapter = mAdapters.get(i).get(); if (adapter != null) { adapter.notifyDataSetChanged(); } else { @@ -485,37 +473,20 @@ public class UserSwitcherController implements Dumpable { } } + @Override public boolean isSimpleUserSwitcher() { return mSimpleUserSwitcher; } - public void setResumeUserOnGuestLogout(boolean resume) { - mResumeUserOnGuestLogout = resume; - } - /** * Returns whether the current user is a system user. */ - public boolean isSystemUser() { + @VisibleForTesting + boolean isSystemUser() { return mUserTracker.getUserId() == UserHandle.USER_SYSTEM; } - public void removeUserId(int userId) { - if (userId == UserHandle.USER_SYSTEM) { - Log.w(TAG, "User " + userId + " could not removed."); - return; - } - if (mUserTracker.getUserId() == userId) { - switchToUserId(UserHandle.USER_SYSTEM); - } - if (mUserManager.removeUser(userId)) { - refreshUsers(UserHandle.USER_NULL); - } - } - - /** - * @return UserRecord for the current user - */ + @Override public @Nullable UserRecord getCurrentUserRecord() { for (int i = 0; i < mUsers.size(); ++i) { UserRecord userRecord = mUsers.get(i); @@ -526,17 +497,7 @@ public class UserSwitcherController implements Dumpable { return null; } - /** - * Notifies that a user has been selected. - * - * <p>This will trigger the right user journeys to create a guest user, switch users, and/or - * navigate to the correct destination. - * - * <p>If a user with the given ID is not found, this method is a no-op. - * - * @param userId The ID of the user to switch to. - * @param dialogShower An optional {@link DialogShower} in case we need to show dialogs. - */ + @Override public void onUserSelected(int userId, @Nullable DialogShower dialogShower) { UserRecord userRecord = mUsers.stream() .filter(x -> x.resolveId() == userId) @@ -549,23 +510,23 @@ public class UserSwitcherController implements Dumpable { onUserListItemClicked(userRecord, dialogShower); } - /** Whether it is allowed to add users while the device is locked. */ - public Flow<Boolean> getAddUsersFromLockScreen() { + @Override + public Flow<Boolean> isAddUsersFromLockScreenEnabled() { return mAddUsersFromLockScreen; } - /** Returns {@code true} if the guest user is configured to always be present on the device. */ + @Override public boolean isGuestUserAutoCreated() { return mGuestUserAutoCreated; } - /** Returns {@code true} if the guest user is currently being reset. */ + @Override public boolean isGuestUserResetting() { return mGuestIsResetting.get(); } - @VisibleForTesting - void onUserListItemClicked(UserRecord record, DialogShower dialogShower) { + @Override + public void onUserListItemClicked(UserRecord record, DialogShower dialogShower) { if (record.isGuest && record.info == null) { createAndSwitchToGuestUser(dialogShower); } else if (record.isAddUser) { @@ -604,7 +565,7 @@ public class UserSwitcherController implements Dumpable { switchToUserId(id); } - protected void switchToUserId(int id) { + private void switchToUserId(int id) { try { if (mView != null) { mInteractionJankMonitor.begin(InteractionJankMonitor.Configuration.Builder @@ -621,7 +582,7 @@ public class UserSwitcherController implements Dumpable { private void showExitGuestDialog(int id, boolean isGuestEphemeral, DialogShower dialogShower) { int newId = UserHandle.USER_SYSTEM; - if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) { + if (mLastNonGuestUser != UserHandle.USER_SYSTEM) { UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser); if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) { newId = info.id; @@ -645,9 +606,7 @@ public class UserSwitcherController implements Dumpable { } } - /** - * Creates and switches to the guest user. - */ + @Override public void createAndSwitchToGuestUser(@Nullable DialogShower dialogShower) { createGuestAsync(guestId -> { // guestId may be USER_NULL if we haven't reloaded the user list yet. @@ -658,9 +617,7 @@ public class UserSwitcherController implements Dumpable { }); } - /** - * Shows the add user dialog. - */ + @Override public void showAddUserDialog(@Nullable DialogShower dialogShower) { if (mAddUserDialog != null && mAddUserDialog.isShowing()) { mAddUserDialog.cancel(); @@ -677,9 +634,7 @@ public class UserSwitcherController implements Dumpable { } } - /** - * Starts an activity to add a supervised user to the device. - */ + @Override public void startSupervisedUserActivity() { final Intent intent = new Intent() .setAction(UserManager.ACTION_CREATE_SUPERVISED_USER) @@ -711,7 +666,7 @@ public class UserSwitcherController implements Dumpable { public void onReceive(Context context, Intent intent) { if (DEBUG) { Log.v(TAG, "Broadcast: a=" + intent.getAction() - + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)); + + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)); } boolean unpauseRefreshUsers = false; @@ -725,8 +680,8 @@ public class UserSwitcherController implements Dumpable { final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); final UserInfo userInfo = mUserManager.getUserInfo(currentId); - final int N = mUsers.size(); - for (int i = 0; i < N; i++) { + final int userCount = mUsers.size(); + for (int i = 0; i < userCount; i++) { UserRecord record = mUsers.get(i); if (record.info == null) continue; boolean shouldBeCurrent = record.info.id == currentId; @@ -805,7 +760,7 @@ public class UserSwitcherController implements Dumpable { pw.println("mGuestUserAutoCreated=" + mGuestUserAutoCreated); } - /** Returns the name of the current user of the phone. */ + @Override public String getCurrentUserName() { if (mUsers.isEmpty()) return null; UserRecord item = mUsers.stream().filter(x -> x.isCurrent).findFirst().orElse(null); @@ -814,40 +769,22 @@ public class UserSwitcherController implements Dumpable { return item.info.name; } + @Override public void onDensityOrFontScaleChanged() { refreshUsers(UserHandle.USER_ALL); } - @VisibleForTesting - public void addAdapter(WeakReference<BaseUserAdapter> adapter) { + @Override + public void addAdapter(WeakReference<BaseUserSwitcherAdapter> adapter) { mAdapters.add(adapter); } - @VisibleForTesting + @Override public ArrayList<UserRecord> getUsers() { return mUsers; } - /** - * Removes guest user and switches to target user. The guest must be the current user and its id - * must be {@code guestUserId}. - * - * <p>If {@code targetUserId} is {@link UserHandle#USER_NULL}, then create a new guest user in - * the foreground, and immediately switch to it. This is used for wiping the current guest and - * replacing it with a new one. - * - * <p>If {@code targetUserId} is specified, then remove the guest in the background while - * switching to {@code targetUserId}. - * - * <p>If device is configured with {@link - * com.android.internal.R.bool.config_guestUserAutoCreated}, then after guest user is removed, a - * new one is created in the background. This has no effect if {@code targetUserId} is {@link - * UserHandle#USER_NULL}. - * - * @param guestUserId id of the guest user to remove - * @param targetUserId id of the user to switch to after guest is removed. If {@link - * UserHandle#USER_NULL}, then switch immediately to the newly created guest user. - */ + @Override public void removeGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId) { UserInfo currentUser = mUserTracker.getUserInfo(); if (currentUser.id != guestUserId) { @@ -894,18 +831,9 @@ public class UserSwitcherController implements Dumpable { } } - /** - * Exits guest user and switches to previous non-guest user. The guest must be the current - * user. - * - * @param guestUserId user id of the guest user to exit - * @param targetUserId user id of the guest user to exit, set to UserHandle#USER_NULL when - * target user id is not known - * @param forceRemoveGuestOnExit true: remove guest before switching user, - * false: remove guest only if its ephemeral, else keep guest - */ + @Override public void exitGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId, - boolean forceRemoveGuestOnExit) { + boolean forceRemoveGuestOnExit) { UserInfo currentUser = mUserTracker.getUserInfo(); if (currentUser.id != guestUserId) { Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")" @@ -921,7 +849,7 @@ public class UserSwitcherController implements Dumpable { int newUserId = UserHandle.USER_SYSTEM; if (targetUserId == UserHandle.USER_NULL) { // when target user is not specified switch to last non guest user - if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) { + if (mLastNonGuestUser != UserHandle.USER_SYSTEM) { UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser); if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) { newUserId = info.id; @@ -959,10 +887,7 @@ public class UserSwitcherController implements Dumpable { } - /** - * Guarantee guest is present only if the device is provisioned. Otherwise, create a content - * observer to wait until the device is provisioned, then schedule the guest creation. - */ + @Override public void schedulePostBootGuestCreation() { if (isDeviceAllowedToAddGuest()) { guaranteeGuestPresent(); @@ -1014,7 +939,7 @@ public class UserSwitcherController implements Dumpable { * @return The multi-user user ID of the newly created guest user, or * {@link UserHandle#USER_NULL} if the guest couldn't be created. */ - public @UserIdInt int createGuest() { + private @UserIdInt int createGuest() { UserInfo guest; try { guest = mUserManager.createGuest(mContext); @@ -1029,135 +954,27 @@ public class UserSwitcherController implements Dumpable { return guest.id; } - /** - * Require a view for jank detection - */ + @Override public void init(View view) { mView = view; } - @VisibleForTesting - public KeyguardStateController getKeyguardStateController() { - return mKeyguardStateController; + @Override + public boolean isKeyguardShowing() { + return mKeyguardStateController.isShowing(); } - /** - * Returns the {@link EnforcedAdmin} for the given record, or {@code null} if there isn't one. - */ + @Override @Nullable public EnforcedAdmin getEnforcedAdmin(UserRecord record) { return mEnforcedAdminByUserRecord.get(record); } - /** - * Returns {@code true} if the given record is disabled by the admin; {@code false} otherwise. - */ + @Override public boolean isDisabledByAdmin(UserRecord record) { return mDisabledByAdmin.contains(record); } - public static abstract class BaseUserAdapter extends BaseAdapter { - - final UserSwitcherController mController; - private final KeyguardStateController mKeyguardStateController; - - protected BaseUserAdapter(UserSwitcherController controller) { - mController = controller; - mKeyguardStateController = controller.getKeyguardStateController(); - controller.addAdapter(new WeakReference<>(this)); - } - - protected ArrayList<UserRecord> getUsers() { - return mController.getUsers(); - } - - public int getUserCount() { - return countUsers(false); - } - - @Override - public int getCount() { - return countUsers(true); - } - - private int countUsers(boolean includeGuest) { - boolean keyguardShowing = mKeyguardStateController.isShowing(); - final int userSize = getUsers().size(); - int count = 0; - for (int i = 0; i < userSize; i++) { - if (getUsers().get(i).isGuest && !includeGuest) { - continue; - } - if (getUsers().get(i).isRestricted && keyguardShowing) { - break; - } - count++; - } - return count; - } - - @Override - public UserRecord getItem(int position) { - return getUsers().get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - /** - * It handles click events on user list items. - * - * If the user switcher is hosted in a dialog, passing a non-null {@link DialogShower} - * will allow animation to and from the parent dialog. - * - */ - public void onUserListItemClicked(UserRecord record, @Nullable DialogShower dialogShower) { - mController.onUserListItemClicked(record, dialogShower); - } - - public void onUserListItemClicked(UserRecord record) { - onUserListItemClicked(record, null); - } - - public String getName(Context context, UserRecord item) { - return getName(context, item, false); - } - - /** - * Returns the name for the given {@link UserRecord}. - */ - public String getName(Context context, UserRecord item, boolean isTablet) { - return LegacyUserUiHelper.getUserRecordName( - context, - item, - mController.isGuestUserAutoCreated(), - mController.isGuestUserResetting(), - isTablet); - } - - protected static ColorFilter getDisabledUserAvatarColorFilter() { - ColorMatrix matrix = new ColorMatrix(); - matrix.setSaturation(0f); // 0 - grayscale - return new ColorMatrixColorFilter(matrix); - } - - protected static Drawable getIconDrawable(Context context, UserRecord item) { - return getIconDrawable(context, item, false); - } - protected static Drawable getIconDrawable(Context context, UserRecord item, - boolean isTablet) { - int iconRes = LegacyUserUiHelper.getUserSwitcherActionIconResourceId( - item.isAddUser, item.isGuest, item.isAddSupervisedUser, isTablet); - return context.getDrawable(iconRes); - } - - public void refresh() { - mController.refreshUsers(UserHandle.USER_NULL); - } - } - private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) { EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext, UserManager.DISALLOW_ADD_USER, mUserTracker.getUserId()); @@ -1178,20 +995,17 @@ public class UserSwitcherController implements Dumpable { defaultSimpleUserSwitcher, UserHandle.USER_SYSTEM) != 0; } + @Override public void startActivity(Intent intent) { mActivityStarter.startActivity(intent, true); } - /** - * Add a subscriber to when user switches. - */ + @Override public void addUserSwitchCallback(UserSwitchCallback callback) { mUserSwitchCallbacks.add(callback); } - /** - * Remove a subscriber to when user switches. - */ + @Override public void removeUserSwitchCallback(UserSwitchCallback callback) { mUserSwitchCallbacks.remove(callback); } @@ -1218,7 +1032,7 @@ public class UserSwitcherController implements Dumpable { // which // helps making the transition faster. if (!mKeyguardStateController.isShowing()) { - mHandler.post(UserSwitcherController.this::notifyAdapters); + mHandler.post(UserSwitcherControllerOldImpl.this::notifyAdapters); } else { notifyAdapters(); } @@ -1367,13 +1181,4 @@ public class UserSwitcherController implements Dumpable { } } - /** - * Callback to for when this controller receives the intent to switch users. - */ - public interface UserSwitchCallback { - /** - * Called when user has switched. - */ - void onUserSwitched(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index 1b7353923ada..b1b45b51d8e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -58,6 +58,8 @@ import com.android.systemui.statusbar.policy.SecurityController; import com.android.systemui.statusbar.policy.SecurityControllerImpl; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; +import com.android.systemui.statusbar.policy.UserSwitcherController; +import com.android.systemui.statusbar.policy.UserSwitcherControllerImpl; import com.android.systemui.statusbar.policy.WalletController; import com.android.systemui.statusbar.policy.WalletControllerImpl; import com.android.systemui.statusbar.policy.ZenModeController; @@ -196,4 +198,8 @@ public interface StatusBarPolicyModule { static DataSaverController provideDataSaverController(NetworkController networkController) { return networkController.getDataSaverController(); } + + /** Binds {@link UserSwitcherController} to its implementation. */ + @Binds + UserSwitcherController bindUserSwitcherController(UserSwitcherControllerImpl impl); } diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index 27746c024dad..00ed3d635fa1 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -36,6 +36,7 @@ import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.doze.DozeHost; +import com.android.systemui.dump.DumpManager; import com.android.systemui.navigationbar.gestural.GestureModule; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -116,9 +117,12 @@ public abstract class TvSystemUIModule { static BatteryController provideBatteryController(Context context, EnhancedEstimates enhancedEstimates, PowerManager powerManager, BroadcastDispatcher broadcastDispatcher, DemoModeController demoModeController, + DumpManager dumpManager, @Main Handler mainHandler, @Background Handler bgHandler) { BatteryController bC = new BatteryControllerImpl(context, enhancedEstimates, powerManager, - broadcastDispatcher, demoModeController, mainHandler, bgHandler); + broadcastDispatcher, demoModeController, + dumpManager, + mainHandler, bgHandler); bC.init(); return bC; } diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt index 5e2dde6be046..108ab43977e9 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt @@ -53,10 +53,8 @@ import com.android.systemui.flags.Flags import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.FalsingManager.LOW_PENALTY import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter import com.android.systemui.statusbar.policy.UserSwitcherController -import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter -import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA -import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA import com.android.systemui.user.data.source.UserRecord import com.android.systemui.user.ui.binder.UserSwitcherViewBinder import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel @@ -66,10 +64,10 @@ import kotlin.math.ceil private const val USER_VIEW = "user_view" -/** - * Support a fullscreen user switcher - */ -open class UserSwitcherActivity @Inject constructor( +/** Support a fullscreen user switcher */ +open class UserSwitcherActivity +@Inject +constructor( private val userSwitcherController: UserSwitcherController, private val broadcastDispatcher: BroadcastDispatcher, private val falsingCollector: FalsingCollector, @@ -86,11 +84,12 @@ open class UserSwitcherActivity @Inject constructor( private lateinit var addButton: View private var addUserRecords = mutableListOf<UserRecord>() private val onBackCallback = OnBackInvokedCallback { finish() } - private val userSwitchedCallback: UserTracker.Callback = object : UserTracker.Callback { - override fun onUserChanged(newUser: Int, userContext: Context) { - finish() + private val userSwitchedCallback: UserTracker.Callback = + object : UserTracker.Callback { + override fun onUserChanged(newUser: Int, userContext: Context) { + finish() + } } - } // When the add users options become available, insert another option to manage users private val manageUserRecord = UserRecord( @@ -114,13 +113,14 @@ open class UserSwitcherActivity @Inject constructor( @VisibleForTesting fun createActivity() { setContentView(R.layout.user_switcher_fullscreen) - window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE - or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) + window.decorView.systemUiVisibility = + (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) if (isUsingModernArchitecture()) { Log.d(TAG, "Using modern architecture.") - val viewModel = ViewModelProvider( - this, viewModelFactory.get())[UserSwitcherViewModel::class.java] + val viewModel = + ViewModelProvider(this, viewModelFactory.get())[UserSwitcherViewModel::class.java] UserSwitcherViewBinder.bind( view = requireViewById(R.id.user_switcher_root), viewModel = viewModel, @@ -136,27 +136,23 @@ open class UserSwitcherActivity @Inject constructor( parent = requireViewById<UserSwitcherRootView>(R.id.user_switcher_root) - parent.touchHandler = object : Gefingerpoken { - override fun onTouchEvent(ev: MotionEvent?): Boolean { - falsingCollector.onTouchEvent(ev) - return false + parent.touchHandler = + object : Gefingerpoken { + override fun onTouchEvent(ev: MotionEvent?): Boolean { + falsingCollector.onTouchEvent(ev) + return false + } } - } - requireViewById<View>(R.id.cancel).apply { - setOnClickListener { - _ -> finish() - } - } + requireViewById<View>(R.id.cancel).apply { setOnClickListener { _ -> finish() } } - addButton = requireViewById<View>(R.id.add).apply { - setOnClickListener { - _ -> showPopupMenu() - } - } + addButton = + requireViewById<View>(R.id.add).apply { setOnClickListener { _ -> showPopupMenu() } } onBackInvokedDispatcher.registerOnBackInvokedCallback( - OnBackInvokedDispatcher.PRIORITY_DEFAULT, onBackCallback) + OnBackInvokedDispatcher.PRIORITY_DEFAULT, + onBackCallback + ) userSwitcherController.init(parent) initBroadcastReceiver() @@ -169,25 +165,30 @@ open class UserSwitcherActivity @Inject constructor( val items = mutableListOf<UserRecord>() addUserRecords.forEach { items.add(it) } - var popupMenuAdapter = ItemAdapter( - this, - R.layout.user_switcher_fullscreen_popup_item, - layoutInflater, - { item: UserRecord -> adapter.getName(this@UserSwitcherActivity, item, true) }, - { item: UserRecord -> adapter.findUserIcon(item, true).mutate().apply { - setTint(resources.getColor( - R.color.user_switcher_fullscreen_popup_item_tint, - getTheme() - )) - } } - ) + var popupMenuAdapter = + ItemAdapter( + this, + R.layout.user_switcher_fullscreen_popup_item, + layoutInflater, + { item: UserRecord -> adapter.getName(this@UserSwitcherActivity, item, true) }, + { item: UserRecord -> + adapter.findUserIcon(item, true).mutate().apply { + setTint( + resources.getColor( + R.color.user_switcher_fullscreen_popup_item_tint, + getTheme() + ) + ) + } + } + ) popupMenuAdapter.addAll(items) - popupMenu = UserSwitcherPopupMenu(this).apply { - setAnchorView(addButton) - setAdapter(popupMenuAdapter) - setOnItemClickListener { - parent: AdapterView<*>, view: View, pos: Int, id: Long -> + popupMenu = + UserSwitcherPopupMenu(this).apply { + setAnchorView(addButton) + setAdapter(popupMenuAdapter) + setOnItemClickListener { parent: AdapterView<*>, view: View, pos: Int, id: Long -> if (falsingManager.isFalseTap(LOW_PENALTY) || !view.isEnabled()) { return@setOnItemClickListener } @@ -206,10 +207,10 @@ open class UserSwitcherActivity @Inject constructor( if (!item.isAddUser) { this@UserSwitcherActivity.finish() } - } + } - show() - } + show() + } } private fun buildUserViews() { @@ -227,8 +228,8 @@ open class UserSwitcherActivity @Inject constructor( val totalWidth = parent.width val userViewCount = adapter.getTotalUserViews() val maxColumns = getMaxColumns(userViewCount) - val horizontalGap = resources - .getDimensionPixelSize(R.dimen.user_switcher_fullscreen_horizontal_gap) + val horizontalGap = + resources.getDimensionPixelSize(R.dimen.user_switcher_fullscreen_horizontal_gap) val totalWidthOfHorizontalGap = (maxColumns - 1) * horizontalGap val maxWidgetDiameter = (totalWidth - totalWidthOfHorizontalGap) / maxColumns @@ -299,14 +300,15 @@ open class UserSwitcherActivity @Inject constructor( } private fun initBroadcastReceiver() { - broadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val action = intent.getAction() - if (Intent.ACTION_SCREEN_OFF.equals(action)) { - finish() + broadcastReceiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val action = intent.getAction() + if (Intent.ACTION_SCREEN_OFF.equals(action)) { + finish() + } } } - } val filter = IntentFilter() filter.addAction(Intent.ACTION_SCREEN_OFF) @@ -322,9 +324,7 @@ open class UserSwitcherActivity @Inject constructor( return flags.isEnabled(Flags.MODERN_USER_SWITCHER_ACTIVITY) } - /** - * Provides views to populate the option menu. - */ + /** Provides views to populate the option menu. */ private class ItemAdapter( val parentContext: Context, val resource: Int, @@ -337,43 +337,27 @@ open class UserSwitcherActivity @Inject constructor( val item = getItem(position) val view = convertView ?: layoutInflater.inflate(resource, parent, false) - view.requireViewById<ImageView>(R.id.icon).apply { - setImageDrawable(iconGetter(item)) - } - view.requireViewById<TextView>(R.id.text).apply { - setText(textGetter(item)) - } + view.requireViewById<ImageView>(R.id.icon).apply { setImageDrawable(iconGetter(item)) } + view.requireViewById<TextView>(R.id.text).apply { setText(textGetter(item)) } return view } } - private inner class UserAdapter : BaseUserAdapter(userSwitcherController) { + private inner class UserAdapter : BaseUserSwitcherAdapter(userSwitcherController) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { val item = getItem(position) var view = convertView as ViewGroup? if (view == null) { - view = layoutInflater.inflate( - R.layout.user_switcher_fullscreen_item, - parent, - false - ) as ViewGroup - } - (view.getChildAt(0) as ImageView).apply { - setImageDrawable(getDrawable(item)) - } - (view.getChildAt(1) as TextView).apply { - setText(getName(getContext(), item)) + view = + layoutInflater.inflate(R.layout.user_switcher_fullscreen_item, parent, false) + as ViewGroup } + (view.getChildAt(0) as ImageView).apply { setImageDrawable(getDrawable(item)) } + (view.getChildAt(1) as TextView).apply { setText(getName(getContext(), item)) } view.setEnabled(item.isSwitchToEnabled) - view.setAlpha( - if (view.isEnabled()) { - USER_SWITCH_ENABLED_ALPHA - } else { - USER_SWITCH_DISABLED_ALPHA - } - ) + UserSwitcherController.setSelectableAlpha(view) view.setTag(USER_VIEW) return view } @@ -401,23 +385,20 @@ open class UserSwitcherActivity @Inject constructor( } fun getTotalUserViews(): Int { - return users.count { item -> - !doNotRenderUserView(item) - } + return users.count { item -> !doNotRenderUserView(item) } } fun doNotRenderUserView(item: UserRecord): Boolean { - return item.isAddUser || - item.isAddSupervisedUser || - item.isGuest && item.info == null + return item.isAddUser || item.isAddSupervisedUser || item.isGuest && item.info == null } private fun getDrawable(item: UserRecord): Drawable { - var drawable = if (item.isGuest) { - getDrawable(R.drawable.ic_account_circle) - } else { - findUserIcon(item) - } + var drawable = + if (item.isGuest) { + getDrawable(R.drawable.ic_account_circle) + } else { + findUserIcon(item) + } drawable.mutate() if (!item.isCurrent && !item.isSwitchToEnabled) { @@ -429,16 +410,16 @@ open class UserSwitcherActivity @Inject constructor( ) } - val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate() - as LayerDrawable - if (item == userSwitcherController.getCurrentUserRecord()) { + val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate() as LayerDrawable + if (item == userSwitcherController.currentUserRecord) { (ld.findDrawableByLayerId(R.id.ring) as GradientDrawable).apply { - val stroke = resources - .getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width) - val color = Utils.getColorAttrDefaultColor( - this@UserSwitcherActivity, - com.android.internal.R.attr.colorAccentPrimary - ) + val stroke = + resources.getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width) + val color = + Utils.getColorAttrDefaultColor( + this@UserSwitcherActivity, + com.android.internal.R.attr.colorAccentPrimary + ) setStroke(stroke, color) } diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt index 305b5ee920a1..035638800f9c 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt @@ -99,7 +99,7 @@ constructor( override val actions: Flow<List<UserActionModel>> = userRecords.map { records -> records.filter { it.isNotUser() }.map { it.toActionModel() } } - override val isActionableWhenLocked: Flow<Boolean> = controller.addUsersFromLockScreen + override val isActionableWhenLocked: Flow<Boolean> = controller.isAddUsersFromLockScreenEnabled override val isGuestUserAutoCreated: Boolean = controller.isGuestUserAutoCreated diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt index d7ad3cefaf06..938417f9dbe3 100644 --- a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt @@ -174,6 +174,7 @@ object UserSwitcherViewBinder { setOnItemClickListener { _, _, position, _ -> val itemPositionExcludingHeader = position - 1 adapter.getItem(itemPositionExcludingHeader).onClicked() + dismiss() } show() diff --git a/packages/SystemUI/src/com/android/systemui/util/recycler/HorizontalSpacerItemDecoration.kt b/packages/SystemUI/src/com/android/systemui/util/recycler/HorizontalSpacerItemDecoration.kt new file mode 100644 index 000000000000..ac931e510139 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/recycler/HorizontalSpacerItemDecoration.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.util.recycler + +import android.graphics.Rect +import android.view.View +import androidx.annotation.Dimension +import androidx.recyclerview.widget.RecyclerView + +/** + * RecyclerView ItemDecorator that adds a horizontal space of the given size between items + * and double that space on the ends. + */ +class HorizontalSpacerItemDecoration(@Dimension private val offset: Int) : + RecyclerView.ItemDecoration() { + + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + val position: Int = parent.getChildAdapterPosition(view) + val itemCount = parent.adapter?.itemCount ?: 0 + + val left = if (position == 0) offset * 2 else offset + val right = if (position == itemCount - 1) offset * 2 else offset + + outRect.set(left, 0, right, 0) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java index 2878ad90835b..0f7e14374e60 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java @@ -16,12 +16,21 @@ package com.android.systemui.wallpapers; +import static android.view.Display.DEFAULT_DISPLAY; + +import static com.android.systemui.flags.Flags.USE_CANVAS_RENDERER; + import android.app.WallpaperColors; +import android.app.WallpaperManager; +import android.content.ComponentCallbacks2; +import android.content.Context; import android.graphics.Bitmap; +import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RectF; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; +import android.os.AsyncTask; import android.os.Handler; import android.os.HandlerThread; import android.os.SystemClock; @@ -31,16 +40,22 @@ import android.util.ArraySet; import android.util.Log; import android.util.MathUtils; import android.util.Size; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.Surface; import android.view.SurfaceHolder; import android.view.WindowManager; import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.wallpapers.canvas.ImageCanvasWallpaperRenderer; import com.android.systemui.wallpapers.gl.EglHelper; import com.android.systemui.wallpapers.gl.ImageWallpaperRenderer; import java.io.FileDescriptor; +import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -59,16 +74,19 @@ public class ImageWallpaper extends WallpaperService { private static final @android.annotation.NonNull RectF LOCAL_COLOR_BOUNDS = new RectF(0, 0, 1, 1); private static final boolean DEBUG = false; + private final ArrayList<RectF> mLocalColorsToAdd = new ArrayList<>(); private final ArraySet<RectF> mColorAreas = new ArraySet<>(); private volatile int mPages = 1; private HandlerThread mWorker; // scaled down version private Bitmap mMiniBitmap; + private final FeatureFlags mFeatureFlags; @Inject - public ImageWallpaper() { + public ImageWallpaper(FeatureFlags featureFlags) { super(); + mFeatureFlags = featureFlags; } @Override @@ -80,7 +98,7 @@ public class ImageWallpaper extends WallpaperService { @Override public Engine onCreateEngine() { - return new GLEngine(); + return mFeatureFlags.isEnabled(USE_CANVAS_RENDERER) ? new CanvasEngine() : new GLEngine(); } @Override @@ -489,4 +507,270 @@ public class ImageWallpaper extends WallpaperService { mRenderer.dump(prefix, fd, out, args); } } + + + class CanvasEngine extends WallpaperService.Engine implements DisplayListener { + + // time [ms] before unloading the wallpaper after it is loaded + private static final int DELAY_FORGET_WALLPAPER = 5000; + + private final Runnable mUnloadWallpaperCallback = this::unloadWallpaper; + + private WallpaperManager mWallpaperManager; + private ImageCanvasWallpaperRenderer mImageCanvasWallpaperRenderer; + private Bitmap mBitmap; + + private Display mDisplay; + private final DisplayInfo mTmpDisplayInfo = new DisplayInfo(); + + private AsyncTask<Void, Void, Bitmap> mLoader; + private boolean mNeedsDrawAfterLoadingWallpaper = false; + + CanvasEngine() { + super(); + setFixedSizeAllowed(true); + setShowForAllUsers(true); + } + + void trimMemory(int level) { + if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW + && level <= ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL + && isBitmapLoaded()) { + if (DEBUG) { + Log.d(TAG, "trimMemory"); + } + unloadWallpaper(); + } + } + + @Override + public void onCreate(SurfaceHolder surfaceHolder) { + if (DEBUG) { + Log.d(TAG, "onCreate"); + } + + mWallpaperManager = getSystemService(WallpaperManager.class); + super.onCreate(surfaceHolder); + + final Context displayContext = getDisplayContext(); + final int displayId = displayContext == null ? DEFAULT_DISPLAY : + displayContext.getDisplayId(); + DisplayManager dm = getSystemService(DisplayManager.class); + if (dm != null) { + mDisplay = dm.getDisplay(displayId); + if (mDisplay == null) { + Log.e(TAG, "Cannot find display! Fallback to default."); + mDisplay = dm.getDisplay(DEFAULT_DISPLAY); + } + } + setOffsetNotificationsEnabled(false); + + mImageCanvasWallpaperRenderer = new ImageCanvasWallpaperRenderer(surfaceHolder); + loadWallpaper(false); + } + + @Override + public void onDestroy() { + super.onDestroy(); + unloadWallpaper(); + } + + @Override + public boolean shouldZoomOutWallpaper() { + return true; + } + + @Override + public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { + if (DEBUG) { + Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height); + } + super.onSurfaceChanged(holder, format, width, height); + mImageCanvasWallpaperRenderer.setSurfaceHolder(holder); + drawFrame(false); + } + + @Override + public void onSurfaceDestroyed(SurfaceHolder holder) { + super.onSurfaceDestroyed(holder); + if (DEBUG) { + Log.i(TAG, "onSurfaceDestroyed"); + } + mImageCanvasWallpaperRenderer.setSurfaceHolder(null); + } + + @Override + public void onSurfaceCreated(SurfaceHolder holder) { + super.onSurfaceCreated(holder); + if (DEBUG) { + Log.i(TAG, "onSurfaceCreated"); + } + mImageCanvasWallpaperRenderer.setSurfaceHolder(holder); + } + + @Override + public void onSurfaceRedrawNeeded(SurfaceHolder holder) { + if (DEBUG) { + Log.d(TAG, "onSurfaceRedrawNeeded"); + } + super.onSurfaceRedrawNeeded(holder); + // At the end of this method we should have drawn into the surface. + // This means that the bitmap should be loaded synchronously if + // it was already unloaded. + if (!isBitmapLoaded()) { + setBitmap(mWallpaperManager.getBitmap(true /* hardware */)); + } + drawFrame(true); + } + + private DisplayInfo getDisplayInfo() { + mDisplay.getDisplayInfo(mTmpDisplayInfo); + return mTmpDisplayInfo; + } + + private void drawFrame(boolean forceRedraw) { + if (!mImageCanvasWallpaperRenderer.isSurfaceHolderLoaded()) { + Log.e(TAG, "attempt to draw a frame without a valid surface"); + return; + } + + if (!isBitmapLoaded()) { + // ensure that we load the wallpaper. + // if the wallpaper is currently loading, this call will have no effect. + loadWallpaper(true); + return; + } + mImageCanvasWallpaperRenderer.drawFrame(mBitmap, forceRedraw); + } + + private void setBitmap(Bitmap bitmap) { + if (bitmap == null) { + Log.e(TAG, "Attempt to set a null bitmap"); + } else if (mBitmap == bitmap) { + Log.e(TAG, "The value of bitmap is the same"); + } else if (bitmap.getWidth() < 1 || bitmap.getHeight() < 1) { + Log.e(TAG, "Attempt to set an invalid wallpaper of length " + + bitmap.getWidth() + "x" + bitmap.getHeight()); + } else { + if (mBitmap != null) { + mBitmap.recycle(); + } + mBitmap = bitmap; + } + } + + private boolean isBitmapLoaded() { + return mBitmap != null && !mBitmap.isRecycled(); + } + + /** + * Loads the wallpaper on background thread and schedules updating the surface frame, + * and if {@code needsDraw} is set also draws a frame. + * + * If loading is already in-flight, subsequent loads are ignored (but needDraw is or-ed to + * the active request). + * + */ + private void loadWallpaper(boolean needsDraw) { + mNeedsDrawAfterLoadingWallpaper |= needsDraw; + if (mLoader != null) { + if (DEBUG) { + Log.d(TAG, "Skipping loadWallpaper, already in flight "); + } + return; + } + mLoader = new AsyncTask<Void, Void, Bitmap>() { + @Override + protected Bitmap doInBackground(Void... params) { + Throwable exception; + try { + Bitmap wallpaper = mWallpaperManager.getBitmap(true /* hardware */); + if (wallpaper != null + && wallpaper.getByteCount() > RecordingCanvas.MAX_BITMAP_SIZE) { + throw new RuntimeException("Wallpaper is too large to draw!"); + } + return wallpaper; + } catch (RuntimeException | OutOfMemoryError e) { + exception = e; + } + + if (isCancelled()) { + return null; + } + + // Note that if we do fail at this, and the default wallpaper can't + // be loaded, we will go into a cycle. Don't do a build where the + // default wallpaper can't be loaded. + Log.w(TAG, "Unable to load wallpaper!", exception); + try { + mWallpaperManager.clear(); + } catch (IOException ex) { + // now we're really screwed. + Log.w(TAG, "Unable reset to default wallpaper!", ex); + } + + if (isCancelled()) { + return null; + } + + try { + return mWallpaperManager.getBitmap(true /* hardware */); + } catch (RuntimeException | OutOfMemoryError e) { + Log.w(TAG, "Unable to load default wallpaper!", e); + } + return null; + } + + @Override + protected void onPostExecute(Bitmap bitmap) { + setBitmap(bitmap); + + if (mNeedsDrawAfterLoadingWallpaper) { + drawFrame(true); + } + + mLoader = null; + mNeedsDrawAfterLoadingWallpaper = false; + scheduleUnloadWallpaper(); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private void unloadWallpaper() { + if (mLoader != null) { + mLoader.cancel(false); + mLoader = null; + } + + if (mBitmap != null) { + mBitmap.recycle(); + } + mBitmap = null; + + final Surface surface = getSurfaceHolder().getSurface(); + surface.hwuiDestroy(); + mWallpaperManager.forgetLoadedWallpaper(); + } + + private void scheduleUnloadWallpaper() { + Handler handler = getMainThreadHandler(); + handler.removeCallbacks(mUnloadWallpaperCallback); + handler.postDelayed(mUnloadWallpaperCallback, DELAY_FORGET_WALLPAPER); + } + + @Override + public void onDisplayAdded(int displayId) { + + } + + @Override + public void onDisplayChanged(int displayId) { + + } + + @Override + public void onDisplayRemoved(int displayId) { + + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRenderer.java new file mode 100644 index 000000000000..fdba16ed2059 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRenderer.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.wallpapers.canvas; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.util.Log; +import android.view.SurfaceHolder; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Helper to draw a wallpaper on a surface. + * It handles the geometry regarding the dimensions of the display and the wallpaper, + * and rescales the surface and the wallpaper accordingly. + */ +public class ImageCanvasWallpaperRenderer { + + private static final String TAG = ImageCanvasWallpaperRenderer.class.getSimpleName(); + private static final boolean DEBUG = false; + + private SurfaceHolder mSurfaceHolder; + //private Bitmap mBitmap = null; + + @VisibleForTesting + static final int MIN_SURFACE_WIDTH = 128; + @VisibleForTesting + static final int MIN_SURFACE_HEIGHT = 128; + + private boolean mSurfaceRedrawNeeded; + + private int mLastSurfaceWidth = -1; + private int mLastSurfaceHeight = -1; + + public ImageCanvasWallpaperRenderer(SurfaceHolder surfaceHolder) { + mSurfaceHolder = surfaceHolder; + } + + /** + * Set the surface holder on which to draw. + * Should be called when the surface holder is created or changed + * @param surfaceHolder the surface on which to draw the wallpaper + */ + public void setSurfaceHolder(SurfaceHolder surfaceHolder) { + mSurfaceHolder = surfaceHolder; + } + + /** + * Check if a surface holder is loaded + * @return true if a valid surfaceHolder has been set. + */ + public boolean isSurfaceHolderLoaded() { + return mSurfaceHolder != null; + } + + /** + * Computes and set the surface dimensions, by using the play and the bitmap dimensions. + * The Bitmap must be loaded before any call to this function + */ + private boolean updateSurfaceSize(Bitmap bitmap) { + int surfaceWidth = Math.max(MIN_SURFACE_WIDTH, bitmap.getWidth()); + int surfaceHeight = Math.max(MIN_SURFACE_HEIGHT, bitmap.getHeight()); + boolean surfaceChanged = + surfaceWidth != mLastSurfaceWidth || surfaceHeight != mLastSurfaceHeight; + if (surfaceChanged) { + /* + Used a fixed size surface, because we are special. We can do + this because we know the current design of window animations doesn't + cause this to break. + */ + mSurfaceHolder.setFixedSize(surfaceWidth, surfaceHeight); + mLastSurfaceWidth = surfaceWidth; + mLastSurfaceHeight = surfaceHeight; + } + return surfaceChanged; + } + + /** + * Draw a the wallpaper on the surface. + * The bitmap and the surface must be loaded before calling + * this function. + * @param forceRedraw redraw the wallpaper even if no changes are detected + */ + public void drawFrame(Bitmap bitmap, boolean forceRedraw) { + + if (bitmap == null || bitmap.isRecycled()) { + Log.e(TAG, "Attempt to draw frame before background is loaded:"); + return; + } + + if (bitmap.getWidth() < 1 || bitmap.getHeight() < 1) { + Log.e(TAG, "Attempt to set an invalid wallpaper of length " + + bitmap.getWidth() + "x" + bitmap.getHeight()); + return; + } + + mSurfaceRedrawNeeded |= forceRedraw; + boolean surfaceChanged = updateSurfaceSize(bitmap); + + boolean redrawNeeded = surfaceChanged || mSurfaceRedrawNeeded; + mSurfaceRedrawNeeded = false; + + if (!redrawNeeded) { + if (DEBUG) { + Log.d(TAG, "Suppressed drawFrame since redraw is not needed "); + } + return; + } + + if (DEBUG) { + Log.d(TAG, "Redrawing wallpaper"); + } + drawWallpaperWithCanvas(bitmap); + } + + @VisibleForTesting + void drawWallpaperWithCanvas(Bitmap bitmap) { + Canvas c = mSurfaceHolder.lockHardwareCanvas(); + if (c != null) { + Rect dest = mSurfaceHolder.getSurfaceFrame(); + Log.i(TAG, "Redrawing in rect: " + dest + " with surface size: " + + mLastSurfaceWidth + "x" + mLastSurfaceHeight); + try { + c.drawBitmap(bitmap, null, dest, null); + } finally { + mSurfaceHolder.unlockCanvasAndPost(c); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 3961a8bcc980..3472cb1c2a7d 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -55,6 +55,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tracing.nano.SystemUiTraceProto; +import com.android.wm.shell.floating.FloatingTasks; import com.android.wm.shell.nano.WmShellTraceProto; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEventCallback; @@ -106,6 +107,7 @@ public final class WMShell extends CoreStartable private final Optional<Pip> mPipOptional; private final Optional<SplitScreen> mSplitScreenOptional; private final Optional<OneHanded> mOneHandedOptional; + private final Optional<FloatingTasks> mFloatingTasksOptional; private final CommandQueue mCommandQueue; private final ConfigurationController mConfigurationController; @@ -166,6 +168,7 @@ public final class WMShell extends CoreStartable Optional<Pip> pipOptional, Optional<SplitScreen> splitScreenOptional, Optional<OneHanded> oneHandedOptional, + Optional<FloatingTasks> floatingTasksOptional, CommandQueue commandQueue, ConfigurationController configurationController, KeyguardStateController keyguardStateController, @@ -190,6 +193,7 @@ public final class WMShell extends CoreStartable mWakefulnessLifecycle = wakefulnessLifecycle; mProtoTracer = protoTracer; mUserTracker = userTracker; + mFloatingTasksOptional = floatingTasksOptional; mSysUiMainExecutor = sysUiMainExecutor; } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt index 2714cf427e19..aca60c033bac 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt @@ -86,14 +86,12 @@ private fun faceModel(user: Int) = KeyguardFaceListenModel( becauseCannotSkipBouncer = false, biometricSettingEnabledForUser = false, bouncerFullyShown = false, - bouncerIsOrWillShow = false, - onlyFaceEnrolled = false, faceAuthenticated = false, faceDisabled = false, faceLockedOut = false, fpLockedOut = false, goingToSleep = false, - keyguardAwakeExcludingBouncerShowing = false, + keyguardAwake = false, keyguardGoingAway = false, listeningForFaceAssistant = false, occludingAppRequestingFaceAuth = false, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index 28e99da49496..43f6f1aac097 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -116,9 +116,7 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); when(mUserSwitcherController.getCurrentUserName()).thenReturn("Test User"); - when(mUserSwitcherController.getKeyguardStateController()) - .thenReturn(mKeyguardStateController); - when(mKeyguardStateController.isShowing()).thenReturn(true); + when(mUserSwitcherController.isKeyguardShowing()).thenReturn(true); mScreenWidth = getUiDevice().getDisplayWidth(); mFakeMeasureSpec = View diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index ae980f58cb3f..9c64c1b06a17 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -212,8 +212,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mBiometricEnabledCallbackArgCaptor; @Captor private ArgumentCaptor<FaceManager.AuthenticationCallback> mAuthenticationCallbackCaptor; - @Captor - private ArgumentCaptor<CancellationSignal> mCancellationSignalCaptor; // Direct executor private final Executor mBackgroundExecutor = Runnable::run; @@ -596,13 +594,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testTriesToAuthenticate_whenBouncer() { - fingerprintIsNotEnrolled(); - faceAuthEnabled(); setKeyguardBouncerVisibility(true); verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean()); - verify(mFaceManager, atLeastOnce()).isHardwareDetected(); - verify(mFaceManager, atLeastOnce()).hasEnrolledTemplates(anyInt()); + verify(mFaceManager).isHardwareDetected(); + verify(mFaceManager).hasEnrolledTemplates(anyInt()); } @Test @@ -1237,9 +1233,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { public void testShouldListenForFace_whenFaceIsAlreadyAuthenticated_returnsFalse() throws RemoteException { // Face auth should run when the following is true. - faceAuthEnabled(); bouncerFullyVisibleAndNotGoingToSleep(); - fingerprintIsNotEnrolled(); keyguardNotGoingAway(); currentUserIsPrimary(); strongAuthNotRequired(); @@ -1266,7 +1260,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext); - // Preconditions for face auth to run + // Face auth should run when the following is true. keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); strongAuthNotRequired(); @@ -1283,7 +1277,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testShouldListenForFace_whenStrongAuthDoesNotAllowScanning_returnsFalse() throws RemoteException { - // Preconditions for face auth to run + // Face auth should run when the following is true. keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); currentUserIsPrimary(); @@ -1304,11 +1298,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testShouldListenForFace_whenBiometricsDisabledForUser_returnsFalse() throws RemoteException { - // Preconditions for face auth to run - faceAuthEnabled(); keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); - fingerprintIsNotEnrolled(); currentUserIsPrimary(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); @@ -1328,11 +1319,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testShouldListenForFace_whenUserCurrentlySwitching_returnsFalse() throws RemoteException { - // Preconditions for face auth to run - faceAuthEnabled(); + // Face auth should run when the following is true. keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); - fingerprintIsNotEnrolled(); currentUserIsPrimary(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); @@ -1351,11 +1340,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testShouldListenForFace_whenSecureCameraLaunched_returnsFalse() throws RemoteException { - // Preconditions for face auth to run - faceAuthEnabled(); keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); - fingerprintIsNotEnrolled(); currentUserIsPrimary(); currentUserDoesNotHaveTrust(); biometricsNotDisabledThroughDevicePolicyManager(); @@ -1374,7 +1360,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testShouldListenForFace_whenOccludingAppRequestsFaceAuth_returnsTrue() throws RemoteException { - // Preconditions for face auth to run + // Face auth should run when the following is true. keyguardNotGoingAway(); bouncerFullyVisibleAndNotGoingToSleep(); currentUserIsPrimary(); @@ -1397,8 +1383,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testShouldListenForFace_whenBouncerShowingAndDeviceIsAwake_returnsTrue() throws RemoteException { - // Preconditions for face auth to run - faceAuthEnabled(); + // Face auth should run when the following is true. keyguardNotGoingAway(); currentUserIsPrimary(); currentUserDoesNotHaveTrust(); @@ -1410,7 +1395,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); bouncerFullyVisibleAndNotGoingToSleep(); - fingerprintIsNotEnrolled(); mTestableLooper.processAllMessages(); assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); @@ -1419,7 +1403,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testShouldListenForFace_whenAuthInterruptIsActive_returnsTrue() throws RemoteException { - // Preconditions for face auth to run + // Face auth should run when the following is true. keyguardNotGoingAway(); currentUserIsPrimary(); currentUserDoesNotHaveTrust(); @@ -1445,7 +1429,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { biometricsNotDisabledThroughDevicePolicyManager(); biometricsEnabledForCurrentUser(); userNotCurrentlySwitching(); - bouncerFullyVisible(); statusBarShadeIsLocked(); mTestableLooper.processAllMessages(); @@ -1459,9 +1442,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { keyguardIsVisible(); assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); statusBarShadeIsNotLocked(); - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); - bouncerNotFullyVisible(); - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); } @@ -1523,44 +1503,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testBouncerVisibility_whenBothFingerprintAndFaceIsEnrolled_stopsFaceAuth() - throws RemoteException { - // Both fingerprint and face are enrolled by default - // Preconditions for face auth to run - keyguardNotGoingAway(); - currentUserIsPrimary(); - currentUserDoesNotHaveTrust(); - biometricsNotDisabledThroughDevicePolicyManager(); - biometricsEnabledForCurrentUser(); - userNotCurrentlySwitching(); - deviceNotGoingToSleep(); - deviceIsInteractive(); - statusBarShadeIsNotLocked(); - keyguardIsVisible(); - - mTestableLooper.processAllMessages(); - clearInvocations(mUiEventLogger); - - assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); - - mKeyguardUpdateMonitor.requestFaceAuth(true, - FaceAuthApiRequestReason.UDFPS_POINTER_DOWN); - - verify(mFaceManager).authenticate(any(), - mCancellationSignalCaptor.capture(), - mAuthenticationCallbackCaptor.capture(), - any(), - anyInt(), - anyBoolean()); - CancellationSignal cancelSignal = mCancellationSignalCaptor.getValue(); - - bouncerWillBeVisibleSoon(); - mTestableLooper.processAllMessages(); - - assertThat(cancelSignal.isCanceled()).isTrue(); - } - - @Test public void testFingerprintCanAuth_whenCancellationNotReceivedAndAuthFailed() { mKeyguardUpdateMonitor.dispatchStartedWakingUp(); mTestableLooper.processAllMessages(); @@ -1623,21 +1565,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, ""); } - private void faceAuthEnabled() { - // this ensures KeyguardUpdateMonitor updates the cached mIsFaceEnrolled flag using the - // face manager mock wire-up in setup() - mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(mCurrentUserId); - } - - private void fingerprintIsNotEnrolled() { - when(mFingerprintManager.hasEnrolledTemplates(mCurrentUserId)).thenReturn(false); - // This updates the cached fingerprint state. - // There is no straightforward API to update the fingerprint state. - // It currently works updates after enrollment changes because something else invokes - // startListeningForFingerprint(), which internally calls this method. - mKeyguardUpdateMonitor.isUnlockWithFingerprintPossible(mCurrentUserId); - } - private void statusBarShadeIsNotLocked() { mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD); } @@ -1744,19 +1671,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.dispatchStartedWakingUp(); } - private void bouncerNotFullyVisible() { - setKeyguardBouncerVisibility(false); - } - private void bouncerFullyVisible() { setKeyguardBouncerVisibility(true); } - private void bouncerWillBeVisibleSoon() { - mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true, false); - mTestableLooper.processAllMessages(); - } - private void setKeyguardBouncerVisibility(boolean isVisible) { mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible, isVisible); mTestableLooper.processAllMessages(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 08b1c28f2112..53e8c6e6d921 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -18,6 +18,7 @@ package com.android.systemui.biometrics; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_MOVE; +import static android.view.MotionEvent.ACTION_UP; import static junit.framework.Assert.assertEquals; @@ -681,6 +682,58 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test + public void aodInterruptCancelTimeoutActionWhenFingerUp() throws RemoteException { + when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); + when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); + + // GIVEN AOD interrupt + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, + BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + mScreenObserver.onScreenTurnedOn(); + mFgExecutor.runAllReady(); + mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); + mFgExecutor.runAllReady(); + + // Configure UdfpsView to accept the ACTION_UP event + when(mUdfpsView.isDisplayConfigured()).thenReturn(true); + + // WHEN ACTION_UP is received + verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0); + mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); + mBiometricsExecutor.runAllReady(); + upEvent.recycle(); + + // Configure UdfpsView to accept the ACTION_DOWN event + when(mUdfpsView.isDisplayConfigured()).thenReturn(false); + + // WHEN ACTION_DOWN is received + MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); + mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); + mBiometricsExecutor.runAllReady(); + downEvent.recycle(); + + // WHEN ACTION_MOVE is received + MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); + mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); + mBiometricsExecutor.runAllReady(); + moveEvent.recycle(); + mFgExecutor.runAllReady(); + + // Configure UdfpsView to accept the finger up event + when(mUdfpsView.isDisplayConfigured()).thenReturn(true); + + // WHEN it times out + mFgExecutor.advanceClockToNext(); + mFgExecutor.runAllReady(); + + // THEN the display should be unconfigured once. If the timeout action is not + // cancelled, the display would be unconfigured twice which would cause two + // FP attempts. + verify(mUdfpsView, times(1)).unconfigureDisplay(); + } + + @Test public void aodInterruptScreenOff() throws RemoteException { // GIVEN screen off mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java index 8e00d10a7d39..faa5db4327a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java @@ -17,7 +17,7 @@ package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER; -import static com.android.systemui.classifier.Classifier.QS_SWIPE; +import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE; import static com.google.common.truth.Truth.assertThat; @@ -106,9 +106,9 @@ public class DistanceClassifierTest extends ClassifierTest { } @Test - public void testPass_QsSwipeAlwaysPasses() { + public void testPass_QsSwipeSideAlwaysPasses() { mClassifier.onTouchEvent(appendDownEvent(1, 1)); - assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 1).isFalse()) + assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 1).isFalse()) .isFalse(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java index 1d61e29cad35..d70d6fc6baa6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java @@ -22,7 +22,8 @@ import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE; import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS; import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN; import static com.android.systemui.classifier.Classifier.PULSE_EXPAND; -import static com.android.systemui.classifier.Classifier.QS_SWIPE; +import static com.android.systemui.classifier.Classifier.QS_SWIPE_NESTED; +import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; import static com.android.systemui.classifier.Classifier.UNLOCK; @@ -323,44 +324,86 @@ public class TypeClassifierTest extends ClassifierTest { } @Test - public void testPass_QsSwipe() { + public void testPass_QsSwipeSide() { when(mDataProvider.isVertical()).thenReturn(false); when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect. when(mDataProvider.isRight()).thenReturn(false); - assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse(); + assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.isUp()).thenReturn(true); when(mDataProvider.isRight()).thenReturn(false); - assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse(); + assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.isUp()).thenReturn(false); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse(); + assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.isUp()).thenReturn(true); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse(); + assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isFalse(); } @Test - public void testFalse_QsSwipe() { + public void testFalse_QsSwipeSide() { when(mDataProvider.isVertical()).thenReturn(true); when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect. when(mDataProvider.isRight()).thenReturn(false); - assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue(); + assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isUp()).thenReturn(true); when(mDataProvider.isRight()).thenReturn(false); - assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue(); + assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isUp()).thenReturn(false); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue(); + assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isUp()).thenReturn(true); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue(); + assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isTrue(); + } + + @Test + public void testPass_QsNestedSwipe() { + when(mDataProvider.isVertical()).thenReturn(true); + + when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect. + when(mDataProvider.isRight()).thenReturn(false); + assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isFalse(); + + when(mDataProvider.isUp()).thenReturn(true); + when(mDataProvider.isRight()).thenReturn(false); + assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isFalse(); + + when(mDataProvider.isUp()).thenReturn(false); + when(mDataProvider.isRight()).thenReturn(true); + assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isFalse(); + + when(mDataProvider.isUp()).thenReturn(true); + when(mDataProvider.isRight()).thenReturn(true); + assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isFalse(); + } + + @Test + public void testFalse_QsNestedSwipe() { + when(mDataProvider.isVertical()).thenReturn(false); + + when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect. + when(mDataProvider.isRight()).thenReturn(false); + assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isTrue(); + + when(mDataProvider.isUp()).thenReturn(true); + when(mDataProvider.isRight()).thenReturn(false); + assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isTrue(); + + when(mDataProvider.isUp()).thenReturn(false); + when(mDataProvider.isRight()).thenReturn(true); + assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isTrue(); + + when(mDataProvider.isUp()).thenReturn(true); + when(mDataProvider.isRight()).thenReturn(true); + assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isTrue(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java index d70467ddeebe..c5a7de410eb0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java @@ -36,6 +36,7 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.systemui.SysuiTestCase; import com.android.systemui.dreams.complication.ComplicationHostViewController; +import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback; @@ -88,6 +89,9 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { @Mock ViewRootImpl mViewRoot; + @Mock + BouncerCallbackInteractor mBouncerCallbackInteractor; + DreamOverlayContainerViewController mController; @Before @@ -110,7 +114,8 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { mResources, MAX_BURN_IN_OFFSET, BURN_IN_PROTECTION_UPDATE_INTERVAL, - MILLIS_UNTIL_FULL_JITTER); + MILLIS_UNTIL_FULL_JITTER, + mBouncerCallbackInteractor); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index 9d4275e65302..eec33ca2ff78 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -23,8 +23,10 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.ComponentName; import android.content.Intent; import android.os.IBinder; +import android.os.RemoteException; import android.service.dreams.DreamService; import android.service.dreams.IDreamOverlay; import android.service.dreams.IDreamOverlayCallback; @@ -57,6 +59,8 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) public class DreamOverlayServiceTest extends SysuiTestCase { + private static final ComponentName LOW_LIGHT_COMPONENT = new ComponentName("package", + "lowlight"); private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); @@ -129,7 +133,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { mDreamOverlayComponentFactory, mStateController, mKeyguardUpdateMonitor, - mUiEventLogger); + mUiEventLogger, + LOW_LIGHT_COMPONENT); } @Test @@ -204,6 +209,22 @@ public class DreamOverlayServiceTest extends SysuiTestCase { } @Test + public void testLowLightSetByIntentExtra() throws RemoteException { + final Intent intent = new Intent(); + intent.putExtra(DreamService.EXTRA_DREAM_COMPONENT, LOW_LIGHT_COMPONENT); + + final IBinder proxy = mService.onBind(intent); + final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); + assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT); + + // Inform the overlay service of dream starting. + overlay.startDream(mWindowParams, mDreamOverlayCallback); + mMainExecutor.runAllReady(); + + verify(mStateController).setLowLightActive(true); + } + + @Test public void testDestroy() { mService.onDestroy(); mMainExecutor.runAllReady(); @@ -211,6 +232,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase { verify(mKeyguardUpdateMonitor).removeCallback(any()); verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED); verify(mStateController).setOverlayActive(false); + verify(mStateController).setLowLightActive(false); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java index 2adf2857a385..d1d32a13589a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java @@ -218,4 +218,20 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase { assertThat(stateController.getComplications(true).contains(complication)) .isTrue(); } + + @Test + public void testNotifyLowLightChanged() { + final DreamOverlayStateController stateController = + new DreamOverlayStateController(mExecutor); + + stateController.addCallback(mCallback); + mExecutor.runAllReady(); + assertThat(stateController.isLowLightActive()).isFalse(); + + stateController.setLowLightActive(true); + + mExecutor.runAllReady(); + verify(mCallback, times(1)).onStateChanged(); + assertThat(stateController.isLowLightActive()).isTrue(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java index 4ebae98d1246..aa021781296a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java @@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -101,6 +102,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { DreamOverlayStatusBarItemsProvider.StatusBarItem mStatusBarItem; @Mock View mStatusBarItemView; + @Mock + DreamOverlayStateController mDreamOverlayStateController; private final Executor mMainExecutor = Runnable::run; @@ -126,7 +129,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { Optional.of(mDreamOverlayNotificationCountProvider), mZenModeController, mStatusBarWindowStateController, - mDreamOverlayStatusBarItemsProvider); + mDreamOverlayStatusBarItemsProvider, + mDreamOverlayStateController); } @Test @@ -137,6 +141,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { verify(mZenModeController).addCallback(any()); verify(mDreamOverlayNotificationCountProvider).addCallback(any()); verify(mDreamOverlayStatusBarItemsProvider).addCallback(any()); + verify(mDreamOverlayStateController).addCallback(any()); } @Test @@ -266,7 +271,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { Optional.empty(), mZenModeController, mStatusBarWindowStateController, - mDreamOverlayStatusBarItemsProvider); + mDreamOverlayStatusBarItemsProvider, + mDreamOverlayStateController); controller.onViewAttached(); verify(mView, never()).showIcon( eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); @@ -305,6 +311,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { verify(mZenModeController).removeCallback(any()); verify(mDreamOverlayNotificationCountProvider).removeCallback(any()); verify(mDreamOverlayStatusBarItemsProvider).removeCallback(any()); + verify(mDreamOverlayStateController).removeCallback(any()); } @Test @@ -458,6 +465,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { @Test public void testStatusBarShownWhenSystemStatusBarHidden() { mController.onViewAttached(); + reset(mView); final ArgumentCaptor<StatusBarWindowStateListener> callbackCapture = ArgumentCaptor.forClass(StatusBarWindowStateListener.class); @@ -471,6 +479,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { public void testUnattachedStatusBarVisibilityUnchangedWhenSystemStatusBarHidden() { mController.onViewAttached(); mController.onViewDetached(); + reset(mView); final ArgumentCaptor<StatusBarWindowStateListener> callbackCapture = ArgumentCaptor.forClass(StatusBarWindowStateListener.class); @@ -493,4 +502,21 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { verify(mView).setExtraStatusBarItemViews(List.of(mStatusBarItemView)); } + + @Test + public void testLowLightHidesStatusBar() { + when(mDreamOverlayStateController.isLowLightActive()).thenReturn(true); + mController.onViewAttached(); + + verify(mView).setVisibility(View.INVISIBLE); + reset(mView); + + when(mDreamOverlayStateController.isLowLightActive()).thenReturn(false); + final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCapture = + ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class); + verify(mDreamOverlayStateController).addCallback(callbackCapture.capture()); + callbackCapture.getValue().onStateChanged(); + + verify(mView).setVisibility(View.VISIBLE); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java index bc944404efe6..522b5b5a8530 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java @@ -16,17 +16,28 @@ package com.android.systemui.dreams.complication; +import static com.android.systemui.flags.Flags.DREAM_MEDIA_TAP_TO_OPEN; + import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.app.PendingIntent; +import android.content.Intent; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; import androidx.test.filters.SmallTest; +import com.android.systemui.ActivityIntentHelper; import com.android.systemui.SysuiTestCase; import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.media.MediaCarouselController; import com.android.systemui.media.dream.MediaDreamComplication; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; import org.junit.Test; @@ -48,21 +59,52 @@ public class DreamMediaEntryComplicationTest extends SysuiTestCase { @Mock private MediaDreamComplication mMediaComplication; + @Mock + private MediaCarouselController mMediaCarouselController; + + @Mock + private ActivityStarter mActivityStarter; + + @Mock + private ActivityIntentHelper mActivityIntentHelper; + + @Mock + private KeyguardStateController mKeyguardStateController; + + @Mock + private NotificationLockscreenUserManager mLockscreenUserManager; + + @Mock + private FeatureFlags mFeatureFlags; + + @Mock + private PendingIntent mPendingIntent; + + private final Intent mIntent = new Intent("android.test.TEST_ACTION"); + private final Integer mCurrentUserId = 99; + @Before public void setup() { MockitoAnnotations.initMocks(this); + when(mFeatureFlags.isEnabled(DREAM_MEDIA_TAP_TO_OPEN)).thenReturn(false); } /** * Ensures clicking media entry chip adds/removes media complication. */ @Test - public void testClick() { + public void testClickToOpenUMO() { final DreamMediaEntryComplication.DreamMediaEntryViewController viewController = new DreamMediaEntryComplication.DreamMediaEntryViewController( mView, mDreamOverlayStateController, - mMediaComplication); + mMediaComplication, + mMediaCarouselController, + mActivityStarter, + mActivityIntentHelper, + mKeyguardStateController, + mLockscreenUserManager, + mFeatureFlags); final ArgumentCaptor<View.OnClickListener> clickListenerCaptor = ArgumentCaptor.forClass(View.OnClickListener.class); @@ -85,10 +127,90 @@ public class DreamMediaEntryComplicationTest extends SysuiTestCase { new DreamMediaEntryComplication.DreamMediaEntryViewController( mView, mDreamOverlayStateController, - mMediaComplication); + mMediaComplication, + mMediaCarouselController, + mActivityStarter, + mActivityIntentHelper, + mKeyguardStateController, + mLockscreenUserManager, + mFeatureFlags); viewController.onViewDetached(); verify(mView).setSelected(false); verify(mDreamOverlayStateController).removeComplication(mMediaComplication); } + + /** + * Ensures clicking media entry chip opens media when flag is set. + */ + @Test + public void testClickToOpenMediaOverLockscreen() { + when(mFeatureFlags.isEnabled(DREAM_MEDIA_TAP_TO_OPEN)).thenReturn(true); + + when(mMediaCarouselController.getCurrentVisibleMediaContentIntent()).thenReturn( + mPendingIntent); + when(mKeyguardStateController.isShowing()).thenReturn(true); + when(mPendingIntent.getIntent()).thenReturn(mIntent); + when(mLockscreenUserManager.getCurrentUserId()).thenReturn(mCurrentUserId); + + final DreamMediaEntryComplication.DreamMediaEntryViewController viewController = + new DreamMediaEntryComplication.DreamMediaEntryViewController( + mView, + mDreamOverlayStateController, + mMediaComplication, + mMediaCarouselController, + mActivityStarter, + mActivityIntentHelper, + mKeyguardStateController, + mLockscreenUserManager, + mFeatureFlags); + viewController.onViewAttached(); + + final ArgumentCaptor<View.OnClickListener> clickListenerCaptor = + ArgumentCaptor.forClass(View.OnClickListener.class); + verify(mView).setOnClickListener(clickListenerCaptor.capture()); + + when(mActivityIntentHelper.wouldShowOverLockscreen(mIntent, mCurrentUserId)).thenReturn( + true); + + clickListenerCaptor.getValue().onClick(mView); + verify(mActivityStarter).startActivity(mIntent, true, null, true); + } + + /** + * Ensures clicking media entry chip opens media when flag is set. + */ + @Test + public void testClickToOpenMediaDismissingLockscreen() { + when(mFeatureFlags.isEnabled(DREAM_MEDIA_TAP_TO_OPEN)).thenReturn(true); + + when(mMediaCarouselController.getCurrentVisibleMediaContentIntent()).thenReturn( + mPendingIntent); + when(mKeyguardStateController.isShowing()).thenReturn(true); + when(mPendingIntent.getIntent()).thenReturn(mIntent); + when(mLockscreenUserManager.getCurrentUserId()).thenReturn(mCurrentUserId); + + final DreamMediaEntryComplication.DreamMediaEntryViewController viewController = + new DreamMediaEntryComplication.DreamMediaEntryViewController( + mView, + mDreamOverlayStateController, + mMediaComplication, + mMediaCarouselController, + mActivityStarter, + mActivityIntentHelper, + mKeyguardStateController, + mLockscreenUserManager, + mFeatureFlags); + viewController.onViewAttached(); + + final ArgumentCaptor<View.OnClickListener> clickListenerCaptor = + ArgumentCaptor.forClass(View.OnClickListener.class); + verify(mView).setOnClickListener(clickListenerCaptor.capture()); + + when(mActivityIntentHelper.wouldShowOverLockscreen(mIntent, mCurrentUserId)).thenReturn( + false); + + clickListenerCaptor.getValue().onClick(mView); + verify(mActivityStarter).postStartActivityDismissingKeyguard(mPendingIntent, null); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt new file mode 100644 index 000000000000..3a61c57d086f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.phone.KeyguardBouncer +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(JUnit4::class) +class BouncerCallbackInteractorTest : SysuiTestCase() { + private val bouncerCallbackInteractor = BouncerCallbackInteractor() + @Mock private lateinit var bouncerExpansionCallback: KeyguardBouncer.BouncerExpansionCallback + @Mock private lateinit var keyguardResetCallback: KeyguardBouncer.KeyguardResetCallback + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + bouncerCallbackInteractor.addBouncerExpansionCallback(bouncerExpansionCallback) + bouncerCallbackInteractor.addKeyguardResetCallback(keyguardResetCallback) + } + + @Test + fun testOnFullyShown() { + bouncerCallbackInteractor.dispatchFullyShown() + verify(bouncerExpansionCallback).onFullyShown() + } + + @Test + fun testOnFullyHidden() { + bouncerCallbackInteractor.dispatchFullyHidden() + verify(bouncerExpansionCallback).onFullyHidden() + } + + @Test + fun testOnExpansionChanged() { + bouncerCallbackInteractor.dispatchExpansionChanged(5f) + verify(bouncerExpansionCallback).onExpansionChanged(5f) + } + + @Test + fun testOnVisibilityChanged() { + bouncerCallbackInteractor.dispatchVisibilityChanged(View.INVISIBLE) + verify(bouncerExpansionCallback).onVisibilityChanged(false) + } + + @Test + fun testOnStartingToHide() { + bouncerCallbackInteractor.dispatchStartingToHide() + verify(bouncerExpansionCallback).onStartingToHide() + } + + @Test + fun testOnStartingToShow() { + bouncerCallbackInteractor.dispatchStartingToShow() + verify(bouncerExpansionCallback).onStartingToShow() + } + + @Test + fun testOnKeyguardReset() { + bouncerCallbackInteractor.dispatchReset() + verify(keyguardResetCallback).onKeyguardReset() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt new file mode 100644 index 000000000000..e6c8dd87d982 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.os.Looper +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.DejankUtils +import com.android.systemui.SysuiTestCase +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.keyguard.DismissCallbackRegistry +import com.android.systemui.keyguard.data.BouncerView +import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository +import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel +import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel +import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN +import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.mockito.any +import com.android.systemui.utils.os.FakeHandler +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Answers +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWithLooper(setAsMainLooper = true) +@RunWith(AndroidTestingRunner::class) +class BouncerInteractorTest : SysuiTestCase() { + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private lateinit var repository: KeyguardBouncerRepository + @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var bouncerView: BouncerView + @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel + @Mock private lateinit var bouncerCallbackInteractor: BouncerCallbackInteractor + @Mock private lateinit var falsingCollector: FalsingCollector + @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry + @Mock private lateinit var keyguardBypassController: KeyguardBypassController + @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + private val mainHandler = FakeHandler(Looper.getMainLooper()) + private lateinit var bouncerInteractor: BouncerInteractor + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + DejankUtils.setImmediate(true) + bouncerInteractor = + BouncerInteractor( + repository, + bouncerView, + mainHandler, + keyguardStateController, + keyguardSecurityModel, + bouncerCallbackInteractor, + falsingCollector, + dismissCallbackRegistry, + keyguardBypassController, + keyguardUpdateMonitor, + ) + `when`(repository.startingDisappearAnimation.value).thenReturn(null) + `when`(repository.show.value).thenReturn(null) + } + + @Test + fun testShow_isScrimmed() { + bouncerInteractor.show(true) + verify(repository).setShowMessage(null) + verify(repository).setOnScreenTurnedOff(false) + verify(repository).setKeyguardAuthenticated(null) + verify(repository).setHide(false) + verify(repository).setStartingToHide(false) + verify(repository).setScrimmed(true) + verify(repository).setExpansion(EXPANSION_VISIBLE) + verify(repository).setShowingSoon(true) + verify(keyguardStateController).notifyBouncerShowing(true) + verify(bouncerCallbackInteractor).dispatchStartingToShow() + verify(repository).setVisible(true) + verify(repository).setShow(any(KeyguardBouncerModel::class.java)) + verify(repository).setShowingSoon(false) + } + + @Test + fun testShow_isNotScrimmed() { + verify(repository, never()).setExpansion(EXPANSION_VISIBLE) + } + + @Test + fun testShow_keyguardIsDone() { + `when`(bouncerView.delegate?.showNextSecurityScreenOrFinish()).thenReturn(true) + verify(keyguardStateController, never()).notifyBouncerShowing(true) + verify(bouncerCallbackInteractor, never()).dispatchStartingToShow() + } + + @Test + fun testHide() { + bouncerInteractor.hide() + verify(falsingCollector).onBouncerHidden() + verify(keyguardStateController).notifyBouncerShowing(false) + verify(repository).setShowingSoon(false) + verify(repository).setOnDismissAction(null) + verify(repository).setVisible(false) + verify(repository).setHide(true) + verify(repository).setShow(null) + } + + @Test + fun testExpansion() { + `when`(repository.expansionAmount.value).thenReturn(0.5f) + bouncerInteractor.setExpansion(0.6f) + verify(repository).setExpansion(0.6f) + verify(bouncerCallbackInteractor).dispatchExpansionChanged(0.6f) + } + + @Test + fun testExpansion_fullyShown() { + `when`(repository.expansionAmount.value).thenReturn(0.5f) + `when`(repository.startingDisappearAnimation.value).thenReturn(null) + bouncerInteractor.setExpansion(EXPANSION_VISIBLE) + verify(falsingCollector).onBouncerShown() + verify(bouncerCallbackInteractor).dispatchFullyShown() + } + + @Test + fun testExpansion_fullyHidden() { + `when`(repository.expansionAmount.value).thenReturn(0.5f) + `when`(repository.startingDisappearAnimation.value).thenReturn(null) + bouncerInteractor.setExpansion(EXPANSION_HIDDEN) + verify(repository).setVisible(false) + verify(repository).setShow(null) + verify(falsingCollector).onBouncerHidden() + verify(bouncerCallbackInteractor).dispatchReset() + verify(bouncerCallbackInteractor).dispatchFullyHidden() + } + + @Test + fun testExpansion_startingToHide() { + `when`(repository.expansionAmount.value).thenReturn(EXPANSION_VISIBLE) + bouncerInteractor.setExpansion(0.1f) + verify(repository).setStartingToHide(true) + verify(bouncerCallbackInteractor).dispatchStartingToHide() + } + + @Test + fun testShowMessage() { + bouncerInteractor.showMessage("abc", null) + verify(repository).setShowMessage(BouncerShowMessageModel("abc", null)) + } + + @Test + fun testDismissAction() { + val onDismissAction = mock(ActivityStarter.OnDismissAction::class.java) + val cancelAction = mock(Runnable::class.java) + bouncerInteractor.setDismissAction(onDismissAction, cancelAction) + verify(repository) + .setOnDismissAction(BouncerCallbackActionsModel(onDismissAction, cancelAction)) + } + + @Test + fun testUpdateResources() { + bouncerInteractor.updateResources() + verify(repository).setResourceUpdateRequests(true) + } + + @Test + fun testNotifyKeyguardAuthenticated() { + bouncerInteractor.notifyKeyguardAuthenticated(true) + verify(repository).setKeyguardAuthenticated(true) + } + + @Test + fun testOnScreenTurnedOff() { + bouncerInteractor.onScreenTurnedOff() + verify(repository).setOnScreenTurnedOff(true) + } + + @Test + fun testSetKeyguardPosition() { + bouncerInteractor.setKeyguardPosition(0f) + verify(repository).setKeyguardPosition(0f) + } + + @Test + fun testNotifyKeyguardAuthenticatedHandled() { + bouncerInteractor.notifyKeyguardAuthenticatedHandled() + verify(repository).setKeyguardAuthenticated(null) + } + + @Test + fun testNotifyUpdatedResources() { + bouncerInteractor.notifyUpdatedResources() + verify(repository).setResourceUpdateRequests(false) + } + + @Test + fun testSetBackButtonEnabled() { + bouncerInteractor.setBackButtonEnabled(true) + verify(repository).setIsBackButtonEnabled(true) + } + + @Test + fun testStartDisappearAnimation() { + val runnable = mock(Runnable::class.java) + bouncerInteractor.startDisappearAnimation(runnable) + verify(repository).setStartDisappearAnimation(any(Runnable::class.java)) + } + + @Test + fun testIsFullShowing() { + `when`(repository.isVisible.value).thenReturn(true) + `when`(repository.expansionAmount.value).thenReturn(EXPANSION_VISIBLE) + `when`(repository.startingDisappearAnimation.value).thenReturn(null) + assertThat(bouncerInteractor.isFullyShowing()).isTrue() + `when`(repository.isVisible.value).thenReturn(false) + assertThat(bouncerInteractor.isFullyShowing()).isFalse() + } + + @Test + fun testIsScrimmed() { + `when`(repository.isScrimmed.value).thenReturn(true) + assertThat(bouncerInteractor.isScrimmed()).isTrue() + `when`(repository.isScrimmed.value).thenReturn(false) + assertThat(bouncerInteractor.isScrimmed()).isFalse() + } + + @Test + fun testIsInTransit() { + `when`(repository.showingSoon.value).thenReturn(true) + assertThat(bouncerInteractor.isInTransit()).isTrue() + `when`(repository.showingSoon.value).thenReturn(false) + assertThat(bouncerInteractor.isInTransit()).isFalse() + `when`(repository.expansionAmount.value).thenReturn(0.5f) + assertThat(bouncerInteractor.isInTransit()).isTrue() + } + + @Test + fun testIsAnimatingAway() { + `when`(repository.startingDisappearAnimation.value).thenReturn(Runnable {}) + assertThat(bouncerInteractor.isAnimatingAway()).isTrue() + `when`(repository.startingDisappearAnimation.value).thenReturn(null) + assertThat(bouncerInteractor.isAnimatingAway()).isFalse() + } + + @Test + fun testWillDismissWithAction() { + `when`(repository.onDismissAction.value?.onDismissAction) + .thenReturn(mock(ActivityStarter.OnDismissAction::class.java)) + assertThat(bouncerInteractor.willDismissWithAction()).isTrue() + `when`(repository.onDismissAction.value?.onDismissAction).thenReturn(null) + assertThat(bouncerInteractor.willDismissWithAction()).isFalse() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt index 5dd1cfcb76e4..e3e3b7413157 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.media +import android.app.PendingIntent import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest @@ -43,6 +44,7 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock +import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.`when` as whenever @@ -366,7 +368,7 @@ class MediaCarouselControllerTest : SysuiTestCase() { playerIndex, mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex ) - assertEquals( playerIndex, 0) + assertEquals(playerIndex, 0) // Replaying the same media player one more time. // And check that the card stays in its position. @@ -402,4 +404,44 @@ class MediaCarouselControllerTest : SysuiTestCase() { visualStabilityCallback.value.onReorderingAllowed() assertEquals(true, result) } + + @Test + fun testGetCurrentVisibleMediaContentIntent() { + val clickIntent1 = mock(PendingIntent::class.java) + val player1 = Triple("player1", + DATA.copy(clickIntent = clickIntent1), + 1000L) + clock.setCurrentTimeMillis(player1.third) + MediaPlayerData.addMediaPlayer(player1.first, + player1.second.copy(notificationKey = player1.first), + panel, clock, isSsReactivated = false) + + assertEquals(mediaCarouselController.getCurrentVisibleMediaContentIntent(), clickIntent1) + + val clickIntent2 = mock(PendingIntent::class.java) + val player2 = Triple("player2", + DATA.copy(clickIntent = clickIntent2), + 2000L) + clock.setCurrentTimeMillis(player2.third) + MediaPlayerData.addMediaPlayer(player2.first, + player2.second.copy(notificationKey = player2.first), + panel, clock, isSsReactivated = false) + + // mediaCarouselScrollHandler.visibleMediaIndex is unchanged (= 0), and the new player is + // added to the front because it was active more recently. + assertEquals(mediaCarouselController.getCurrentVisibleMediaContentIntent(), clickIntent2) + + val clickIntent3 = mock(PendingIntent::class.java) + val player3 = Triple("player3", + DATA.copy(clickIntent = clickIntent3), + 500L) + clock.setCurrentTimeMillis(player3.third) + MediaPlayerData.addMediaPlayer(player3.first, + player3.second.copy(notificationKey = player3.first), + panel, clock, isSsReactivated = false) + + // mediaCarouselScrollHandler.visibleMediaIndex is unchanged (= 0), and the new player is + // added to the end because it was active less recently. + assertEquals(mediaCarouselController.getCurrentVisibleMediaContentIntent(), clickIntent2) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java index 22ecb4b93743..5f643363bd2e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java @@ -23,6 +23,8 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.WallpaperColors; +import android.graphics.Bitmap; import android.graphics.drawable.Icon; import android.testing.AndroidTestingRunner; import android.view.View; @@ -102,6 +104,18 @@ public class MediaOutputAdapterTest extends SysuiTestCase { } @Test + public void getItemId_validPosition_returnCorrespondingId() { + assertThat(mMediaOutputAdapter.getItemId(0)).isEqualTo(mMediaDevices.get( + 0).getId().hashCode()); + } + + @Test + public void getItemId_invalidPosition_returnPosition() { + int invalidPosition = mMediaDevices.size() + 1; + assertThat(mMediaOutputAdapter.getItemId(invalidPosition)).isEqualTo(invalidPosition); + } + + @Test public void onBindViewHolder_bindPairNew_verifyView() { mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2); @@ -155,6 +169,33 @@ public class MediaOutputAdapterTest extends SysuiTestCase { } @Test + public void onBindViewHolder_bindConnectedDeviceWithMutingExpectedDeviceExist_verifyView() { + when(mMediaOutputController.hasMutingExpectedDevice()).thenReturn(true); + when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(false); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + + assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1); + } + + @Test + public void onBindViewHolder_isMutingExpectedDevice_verifyView() { + when(mMediaDevice1.isMutingExpectedDevice()).thenReturn(true); + when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(false); + when(mMediaOutputController.isActiveRemoteDevice(mMediaDevice1)).thenReturn(false); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + + assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1); + } + + @Test public void onBindViewHolder_initSeekbar_setsVolume() { when(mMediaDevice1.getMaxVolume()).thenReturn(TEST_MAX_VOLUME); when(mMediaDevice1.getCurrentVolume()).thenReturn(TEST_CURRENT_VOLUME); @@ -165,6 +206,20 @@ public class MediaOutputAdapterTest extends SysuiTestCase { } @Test + public void onBindViewHolder_bindSelectableDevice_verifyView() { + List<MediaDevice> selectableDevices = new ArrayList<>(); + selectableDevices.add(mMediaDevice2); + when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(selectableDevices); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1); + + assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2); + } + + @Test public void onBindViewHolder_bindNonActiveConnectedDevice_verifyView() { mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1); @@ -223,6 +278,22 @@ public class MediaOutputAdapterTest extends SysuiTestCase { } @Test + public void onBindViewHolder_bindGroupingDevice_verifyView() { + when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(false); + when(mMediaDevice1.getState()).thenReturn( + LocalMediaManager.MediaDeviceState.STATE_GROUPING); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + + assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1); + assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE); + } + + @Test public void onBindViewHolder_inTransferring_bindNonTransferringDevice_verifyView() { when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(true); when(mMediaDevice2.getState()).thenReturn( @@ -256,6 +327,31 @@ public class MediaOutputAdapterTest extends SysuiTestCase { } @Test + public void onItemClick_clicksWithMutingExpectedDeviceExist_cancelsMuteAwaitConnection() { + when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(false); + when(mMediaOutputController.hasMutingExpectedDevice()).thenReturn(true); + when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(false); + when(mMediaDevice1.isMutingExpectedDevice()).thenReturn(false); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + + mViewHolder.mContainerLayout.performClick(); + + verify(mMediaOutputController).cancelMuteAwaitConnection(); + } + + @Test + public void onItemClick_clicksSelectableDevice_triggerGrouping() { + List<MediaDevice> selectableDevices = new ArrayList<>(); + selectableDevices.add(mMediaDevice2); + when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(selectableDevices); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1); + + mViewHolder.mContainerLayout.performClick(); + + verify(mMediaOutputController).addDeviceToPlayMedia(mMediaDevice2); + } + + @Test public void onItemClick_onGroupActionTriggered_verifySeekbarDisabled() { when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices); List<MediaDevice> selectableDevices = new ArrayList<>(); @@ -280,4 +376,14 @@ public class MediaOutputAdapterTest extends SysuiTestCase { assertThat(mViewHolder.mSeekBar.isEnabled()).isTrue(); } + + @Test + public void updateColorScheme_triggerController() { + WallpaperColors wallpaperColors = WallpaperColors.fromBitmap( + Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)); + + mMediaOutputAdapter.updateColorScheme(wallpaperColors, true); + + verify(mMediaOutputController).setCurrentColorScheme(wallpaperColors, true); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java index eb8ecae305bc..9be201e99b2b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java @@ -241,46 +241,29 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { } @Test - public void onStart_isBroadcasting_verifyRegisterLeBroadcastServiceCallBack() { + public void whenBroadcasting_verifyLeBroadcastServiceCallBackIsRegisteredAndUnregistered() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); mIsBroadcasting = true; mMediaOutputBaseDialogImpl.onStart(); - verify(mLocalBluetoothLeBroadcast).registerServiceCallBack(any(), any()); - } - - @Test - public void onStart_notBroadcasting_noRegisterLeBroadcastServiceCallBack() { - when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( - mLocalBluetoothLeBroadcast); - mIsBroadcasting = false; - - mMediaOutputBaseDialogImpl.onStart(); - - verify(mLocalBluetoothLeBroadcast, never()).registerServiceCallBack(any(), any()); - } - - @Test - public void onStart_isBroadcasting_verifyUnregisterLeBroadcastServiceCallBack() { - when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( - mLocalBluetoothLeBroadcast); - mIsBroadcasting = true; mMediaOutputBaseDialogImpl.onStop(); - verify(mLocalBluetoothLeBroadcast).unregisterServiceCallBack(any()); } @Test - public void onStop_notBroadcasting_noUnregisterLeBroadcastServiceCallBack() { + public void + whenNotBroadcasting_verifyLeBroadcastServiceCallBackIsNotRegisteredOrUnregistered() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); mIsBroadcasting = false; + mMediaOutputBaseDialogImpl.onStart(); mMediaOutputBaseDialogImpl.onStop(); + verify(mLocalBluetoothLeBroadcast, never()).registerServiceCallBack(any(), any()); verify(mLocalBluetoothLeBroadcast, never()).unregisterServiceCallBack(any()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java index 6dcf8024eca8..cb31fde26bf2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java @@ -35,6 +35,7 @@ import android.app.KeyguardManager; import android.app.Notification; import android.content.Context; import android.graphics.drawable.Icon; +import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.media.MediaDescription; import android.media.MediaMetadata; @@ -279,6 +280,203 @@ public class MediaOutputControllerTest extends SysuiTestCase { } @Test + public void onDeviceListUpdate_isRefreshing_updatesNeedRefreshToTrue() { + mMediaOutputController.start(mCb); + reset(mCb); + mMediaOutputController.mIsRefreshing = true; + + mMediaOutputController.onDeviceListUpdate(mMediaDevices); + + assertThat(mMediaOutputController.mNeedRefresh).isTrue(); + } + + @Test + public void cancelMuteAwaitConnection_cancelsWithMediaManager() { + when(mAudioManager.getMutingExpectedDevice()).thenReturn(mock(AudioDeviceAttributes.class)); + mMediaOutputController.start(mCb); + reset(mCb); + + mMediaOutputController.cancelMuteAwaitConnection(); + + verify(mAudioManager).cancelMuteAwaitConnection(any()); + } + + @Test + public void cancelMuteAwaitConnection_audioManagerIsNull_noAction() { + when(mAudioManager.getMutingExpectedDevice()).thenReturn(null); + mMediaOutputController.start(mCb); + reset(mCb); + mMediaOutputController.cancelMuteAwaitConnection(); + + verify(mAudioManager, never()).cancelMuteAwaitConnection(any()); + } + + @Test + public void getAppSourceName_packageNameIsNull_returnsNull() { + MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext, + "", + mMediaSessionManager, mLocalBluetoothManager, mStarter, + mNotifCollection, mDialogLaunchAnimator, + Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager, + mKeyguardManager); + testMediaOutputController.start(mCb); + reset(mCb); + + testMediaOutputController.getAppSourceName(); + + assertThat(testMediaOutputController.getAppSourceName()).isNull(); + } + + @Test + public void isActiveItem_deviceNotConnected_returnsFalse() { + when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice2); + + assertThat(mMediaOutputController.isActiveItem(mMediaDevice1)).isFalse(); + } + + @Test + public void getNotificationSmallIcon_packageNameIsNull_returnsNull() { + MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext, + "", + mMediaSessionManager, mLocalBluetoothManager, mStarter, + mNotifCollection, mDialogLaunchAnimator, + Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager, + mKeyguardManager); + testMediaOutputController.start(mCb); + reset(mCb); + + testMediaOutputController.getAppSourceName(); + + assertThat(testMediaOutputController.getNotificationSmallIcon()).isNull(); + } + + @Test + public void refreshDataSetIfNeeded_needRefreshIsTrue_setsToFalse() { + mMediaOutputController.start(mCb); + reset(mCb); + mMediaOutputController.mNeedRefresh = true; + + mMediaOutputController.refreshDataSetIfNeeded(); + + assertThat(mMediaOutputController.mNeedRefresh).isFalse(); + } + + @Test + public void isCurrentConnectedDeviceRemote_containsFeatures_returnsTrue() { + when(mMediaDevice1.getFeatures()).thenReturn( + ImmutableList.of(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)); + when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1); + + assertThat(mMediaOutputController.isCurrentConnectedDeviceRemote()).isTrue(); + } + + @Test + public void addDeviceToPlayMedia_triggersFromLocalMediaManager() { + MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext, + null, + mMediaSessionManager, mLocalBluetoothManager, mStarter, + mNotifCollection, mDialogLaunchAnimator, + Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager, + mKeyguardManager); + + LocalMediaManager testLocalMediaManager = spy(testMediaOutputController.mLocalMediaManager); + testMediaOutputController.mLocalMediaManager = testLocalMediaManager; + + testMediaOutputController.addDeviceToPlayMedia(mMediaDevice2); + + verify(testLocalMediaManager).addDeviceToPlayMedia(mMediaDevice2); + } + + @Test + public void removeDeviceFromPlayMedia_triggersFromLocalMediaManager() { + MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext, + null, + mMediaSessionManager, mLocalBluetoothManager, mStarter, + mNotifCollection, mDialogLaunchAnimator, + Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager, + mKeyguardManager); + + LocalMediaManager testLocalMediaManager = spy(testMediaOutputController.mLocalMediaManager); + testMediaOutputController.mLocalMediaManager = testLocalMediaManager; + + testMediaOutputController.removeDeviceFromPlayMedia(mMediaDevice2); + + verify(testLocalMediaManager).removeDeviceFromPlayMedia(mMediaDevice2); + } + + @Test + public void getDeselectableMediaDevice_triggersFromLocalMediaManager() { + mMediaOutputController.getDeselectableMediaDevice(); + + verify(mLocalMediaManager).getDeselectableMediaDevice(); + } + + @Test + public void adjustSessionVolume_adjustWithoutId_triggersFromLocalMediaManager() { + int testVolume = 10; + mMediaOutputController.adjustSessionVolume(testVolume); + + verify(mLocalMediaManager).adjustSessionVolume(testVolume); + } + + @Test + public void getSessionVolumeMax_triggersFromLocalMediaManager() { + mMediaOutputController.getSessionVolumeMax(); + + verify(mLocalMediaManager).getSessionVolumeMax(); + } + + @Test + public void getSessionVolume_triggersFromLocalMediaManager() { + mMediaOutputController.getSessionVolume(); + + verify(mLocalMediaManager).getSessionVolume(); + } + + @Test + public void getSessionName_triggersFromLocalMediaManager() { + mMediaOutputController.getSessionName(); + + verify(mLocalMediaManager).getSessionName(); + } + + @Test + public void releaseSession_triggersFromLocalMediaManager() { + mMediaOutputController.releaseSession(); + + verify(mLocalMediaManager).releaseSession(); + } + + @Test + public void isAnyDeviceTransferring_noDevicesStateIsConnecting_returnsFalse() { + mMediaOutputController.start(mCb); + reset(mCb); + + mMediaOutputController.onDeviceListUpdate(mMediaDevices); + + assertThat(mMediaOutputController.isAnyDeviceTransferring()).isFalse(); + } + + @Test + public void isAnyDeviceTransferring_deviceStateIsConnecting_returnsTrue() { + when(mMediaDevice1.getState()).thenReturn( + LocalMediaManager.MediaDeviceState.STATE_CONNECTING); + mMediaOutputController.start(mCb); + reset(mCb); + + mMediaOutputController.onDeviceListUpdate(mMediaDevices); + + assertThat(mMediaOutputController.isAnyDeviceTransferring()).isTrue(); + } + + @Test + public void isPlaying_stateIsNull() { + when(mMediaController.getPlaybackState()).thenReturn(null); + + assertThat(mMediaOutputController.isPlaying()).isFalse(); + } + + @Test public void onSelectedDeviceStateChanged_verifyCallback() { when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice2); mMediaOutputController.start(mCb); @@ -535,6 +733,44 @@ public class MediaOutputControllerTest extends SysuiTestCase { } @Test + public void getNotificationSmallIcon_withoutSmallIcon_returnsNull() { + final List<NotificationEntry> entryList = new ArrayList<>(); + final NotificationEntry entry = mock(NotificationEntry.class); + final StatusBarNotification sbn = mock(StatusBarNotification.class); + final Notification notification = mock(Notification.class); + entryList.add(entry); + + when(mNotifCollection.getAllNotifs()).thenReturn(entryList); + when(entry.getSbn()).thenReturn(sbn); + when(sbn.getNotification()).thenReturn(notification); + when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(notification.isMediaNotification()).thenReturn(true); + when(notification.getSmallIcon()).thenReturn(null); + + assertThat(mMediaOutputController.getNotificationSmallIcon()).isNull(); + } + + @Test + public void getNotificationSmallIcon_withPackageNameAndMediaSession_returnsIconCompat() { + final List<NotificationEntry> entryList = new ArrayList<>(); + final NotificationEntry entry = mock(NotificationEntry.class); + final StatusBarNotification sbn = mock(StatusBarNotification.class); + final Notification notification = mock(Notification.class); + final Icon icon = mock(Icon.class); + entryList.add(entry); + + when(mNotifCollection.getAllNotifs()).thenReturn(entryList); + when(entry.getSbn()).thenReturn(sbn); + when(sbn.getNotification()).thenReturn(notification); + when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(notification.isMediaNotification()).thenReturn(true); + when(notification.getSmallIcon()).thenReturn(icon); + + assertThat(mMediaOutputController.getNotificationSmallIcon()).isInstanceOf( + IconCompat.class); + } + + @Test public void isVolumeControlEnabled_isCastWithVolumeFixed_returnsFalse() { when(mMediaDevice1.getDeviceType()).thenReturn( MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java index 9557513775f8..bae3569cdc93 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java @@ -25,7 +25,10 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.KeyguardManager; +import android.graphics.Bitmap; import android.media.AudioManager; +import android.media.MediaDescription; +import android.media.MediaMetadata; import android.media.MediaRoute2Info; import android.media.session.MediaController; import android.media.session.MediaSessionManager; @@ -43,6 +46,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastSender; @@ -82,6 +86,8 @@ public class MediaOutputDialogTest extends SysuiTestCase { private final CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class); private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class); private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class); + private final MediaMetadata mMediaMetadata = mock(MediaMetadata.class); + private final MediaDescription mMediaDescription = mock(MediaDescription.class); private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock( NearbyMediaDevicesManager.class); private final AudioManager mAudioManager = mock(AudioManager.class); @@ -100,6 +106,8 @@ public class MediaOutputDialogTest extends SysuiTestCase { when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState); when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_NONE); when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE); + when(mMediaController.getMetadata()).thenReturn(mMediaMetadata); + when(mMediaMetadata.getDescription()).thenReturn(mMediaDescription); mMediaControllers.add(mMediaController); when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers); @@ -207,6 +215,80 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test + public void getHeaderIcon_getFromMediaControllerMetaData() { + int testWidth = 10; + int testHeight = 20; + when(mMediaDescription.getIconBitmap()) + .thenReturn(Bitmap.createBitmap(testWidth, testHeight, Bitmap.Config.ARGB_8888)); + + assertThat(mMediaOutputDialog.getHeaderIcon().getBitmap().getHeight()).isEqualTo( + testHeight); + assertThat(mMediaOutputDialog.getHeaderIcon().getBitmap().getWidth()).isEqualTo(testWidth); + } + + @Test + public void getHeaderText_getFromMediaControllerMetaData() { + String testTitle = "test title"; + when(mMediaDescription.getTitle()) + .thenReturn(testTitle); + assertThat(mMediaOutputDialog.getHeaderText().toString()).isEqualTo(testTitle); + } + + @Test + public void getHeaderSubtitle_getFromMediaControllerMetaData() { + String testSubtitle = "test title"; + when(mMediaDescription.getSubtitle()) + .thenReturn(testSubtitle); + + assertThat(mMediaOutputDialog.getHeaderSubtitle().toString()).isEqualTo(testSubtitle); + } + + @Test + public void getStopButtonText_notSupportsBroadcast_returnsDefaultText() { + String stopText = mContext.getText(R.string.keyboard_key_media_stop).toString(); + MediaOutputController mockMediaOutputController = mock(MediaOutputController.class); + when(mockMediaOutputController.isBroadcastSupported()).thenReturn(false); + + MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false, mBroadcastSender, + mockMediaOutputController, mUiEventLogger); + testDialog.show(); + + assertThat(testDialog.getStopButtonText().toString()).isEqualTo(stopText); + } + + @Test + public void getStopButtonText_supportsBroadcast_returnsBroadcastText() { + String stopText = mContext.getText(R.string.media_output_broadcast).toString(); + MediaDevice mMediaDevice = mock(MediaDevice.class); + MediaOutputController mockMediaOutputController = mock(MediaOutputController.class); + when(mockMediaOutputController.isBroadcastSupported()).thenReturn(true); + when(mockMediaOutputController.getCurrentConnectedMediaDevice()).thenReturn(mMediaDevice); + when(mockMediaOutputController.isBluetoothLeDevice(any())).thenReturn(true); + when(mockMediaOutputController.isPlaying()).thenReturn(true); + when(mockMediaOutputController.isBluetoothLeBroadcastEnabled()).thenReturn(false); + MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false, mBroadcastSender, + mockMediaOutputController, mUiEventLogger); + testDialog.show(); + + assertThat(testDialog.getStopButtonText().toString()).isEqualTo(stopText); + } + + @Test + public void onStopButtonClick_notPlaying_releaseSession() { + MediaOutputController mockMediaOutputController = mock(MediaOutputController.class); + when(mockMediaOutputController.isBroadcastSupported()).thenReturn(false); + when(mockMediaOutputController.getCurrentConnectedMediaDevice()).thenReturn(null); + when(mockMediaOutputController.isPlaying()).thenReturn(false); + MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false, mBroadcastSender, + mockMediaOutputController, mUiEventLogger); + testDialog.show(); + + testDialog.onStopButtonClick(); + + verify(mockMediaOutputController).releaseSession(); + } + + @Test // Check the visibility metric logging by creating a new MediaOutput dialog, // and verify if the calling times increases. public void onCreate_ShouldLogVisibility() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java index 0bfc0344b521..2f52950a9ee4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java @@ -16,7 +16,7 @@ package com.android.systemui.media.dream; -import static com.android.systemui.flags.Flags.MEDIA_DREAM_COMPLICATION; +import static com.android.systemui.flags.Flags.DREAM_MEDIA_COMPLICATION; import static org.mockito.AdditionalMatchers.not; import static org.mockito.ArgumentMatchers.any; @@ -68,7 +68,7 @@ public class MediaDreamSentinelTest extends SysuiTestCase { public void setup() { MockitoAnnotations.initMocks(this); - when(mFeatureFlags.isEnabled(MEDIA_DREAM_COMPLICATION)).thenReturn(true); + when(mFeatureFlags.isEnabled(DREAM_MEDIA_COMPLICATION)).thenReturn(true); } @Test @@ -137,7 +137,7 @@ public class MediaDreamSentinelTest extends SysuiTestCase { @Test public void testOnMediaDataLoaded_mediaComplicationDisabled_doesNotAddComplication() { - when(mFeatureFlags.isEnabled(MEDIA_DREAM_COMPLICATION)).thenReturn(false); + when(mFeatureFlags.isEnabled(DREAM_MEDIA_COMPLICATION)).thenReturn(false); final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager, mDreamOverlayStateController, mMediaEntryComplication, mFeatureFlags); diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt new file mode 100644 index 000000000000..00b1f3268bae --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt @@ -0,0 +1,120 @@ +package com.android.systemui.mediaprojection.appselector + +import android.content.ComponentName +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.mediaprojection.appselector.data.RecentTask +import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider +import com.android.systemui.util.mockito.mock +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.verify + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class MediaProjectionAppSelectorControllerTest : SysuiTestCase() { + + private val taskListProvider = TestRecentTaskListProvider() + private val scope = CoroutineScope(Dispatchers.Unconfined) + private val appSelectorComponentName = ComponentName("com.test", "AppSelector") + + private val view: MediaProjectionAppSelectorView = mock() + + private val controller = MediaProjectionAppSelectorController( + taskListProvider, + scope, + appSelectorComponentName + ) + + @Test + fun initNoRecentTasks_bindsEmptyList() { + taskListProvider.tasks = emptyList() + + controller.init(view) + + verify(view).bind(emptyList()) + } + + @Test + fun initOneRecentTask_bindsList() { + taskListProvider.tasks = listOf( + createRecentTask(taskId = 1) + ) + + controller.init(view) + + verify(view).bind( + listOf( + createRecentTask(taskId = 1) + ) + ) + } + + @Test + fun initMultipleRecentTasksWithoutAppSelectorTask_bindsListInTheSameOrder() { + val tasks = listOf( + createRecentTask(taskId = 1), + createRecentTask(taskId = 2), + createRecentTask(taskId = 3), + ) + taskListProvider.tasks = tasks + + controller.init(view) + + verify(view).bind( + listOf( + createRecentTask(taskId = 1), + createRecentTask(taskId = 2), + createRecentTask(taskId = 3), + ) + ) + } + + @Test + fun initRecentTasksWithAppSelectorTasks_bindsAppSelectorTasksAtTheEnd() { + val tasks = listOf( + createRecentTask(taskId = 1), + createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName), + createRecentTask(taskId = 3), + createRecentTask(taskId = 4, topActivityComponent = appSelectorComponentName), + createRecentTask(taskId = 5), + ) + taskListProvider.tasks = tasks + + controller.init(view) + + verify(view).bind( + listOf( + createRecentTask(taskId = 1), + createRecentTask(taskId = 3), + createRecentTask(taskId = 5), + createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName), + createRecentTask(taskId = 4, topActivityComponent = appSelectorComponentName), + ) + ) + } + + private fun createRecentTask( + taskId: Int, + topActivityComponent: ComponentName? = null + ): RecentTask { + return RecentTask( + taskId = taskId, + topActivityComponent = topActivityComponent, + baseIntentComponent = ComponentName("com", "Test"), + userId = 0, + colorBackground = 0 + ) + } + + private class TestRecentTaskListProvider : RecentTaskListProvider { + + var tasks: List<RecentTask> = emptyList() + + override suspend fun loadRecentTasks(): List<RecentTask> = tasks + + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt new file mode 100644 index 000000000000..939af16d81cd --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt @@ -0,0 +1,112 @@ +package com.android.systemui.mediaprojection.appselector.data + +import android.app.ActivityManager.RecentTaskInfo +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.android.wm.shell.recents.RecentTasks +import com.android.wm.shell.util.GroupedRecentTaskInfo +import com.google.common.truth.Truth.assertThat +import java.util.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import org.junit.Test +import org.junit.runner.RunWith +import java.util.function.Consumer + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class ShellRecentTaskListProviderTest : SysuiTestCase() { + + private val dispatcher = Dispatchers.Unconfined + private val recentTasks: RecentTasks = mock() + private val recentTaskListProvider = + ShellRecentTaskListProvider(dispatcher, Runnable::run, Optional.of(recentTasks)) + + @Test + fun loadRecentTasks_oneTask_returnsTheSameTask() { + givenRecentTasks(createSingleTask(taskId = 1)) + + val result = runBlocking { recentTaskListProvider.loadRecentTasks() } + + assertThat(result).containsExactly(createRecentTask(taskId = 1)) + } + + @Test + fun loadRecentTasks_multipleTasks_returnsTheSameTasks() { + givenRecentTasks( + createSingleTask(taskId = 1), + createSingleTask(taskId = 2), + createSingleTask(taskId = 3), + ) + + val result = runBlocking { recentTaskListProvider.loadRecentTasks() } + + assertThat(result) + .containsExactly( + createRecentTask(taskId = 1), + createRecentTask(taskId = 2), + createRecentTask(taskId = 3), + ) + } + + @Test + fun loadRecentTasks_groupedTask_returnsUngroupedTasks() { + givenRecentTasks(createTaskPair(taskId1 = 1, taskId2 = 2)) + + val result = runBlocking { recentTaskListProvider.loadRecentTasks() } + + assertThat(result) + .containsExactly(createRecentTask(taskId = 1), createRecentTask(taskId = 2)) + } + + @Test + fun loadRecentTasks_mixedSingleAndGroupedTask_returnsUngroupedTasks() { + givenRecentTasks( + createSingleTask(taskId = 1), + createTaskPair(taskId1 = 2, taskId2 = 3), + createSingleTask(taskId = 4), + createTaskPair(taskId1 = 5, taskId2 = 6), + ) + + val result = runBlocking { recentTaskListProvider.loadRecentTasks() } + + assertThat(result) + .containsExactly( + createRecentTask(taskId = 1), + createRecentTask(taskId = 2), + createRecentTask(taskId = 3), + createRecentTask(taskId = 4), + createRecentTask(taskId = 5), + createRecentTask(taskId = 6), + ) + } + + @Suppress("UNCHECKED_CAST") + private fun givenRecentTasks(vararg tasks: GroupedRecentTaskInfo) { + whenever(recentTasks.getRecentTasks(any(), any(), any(), any(), any())).thenAnswer { + val consumer = it.arguments.last() as Consumer<List<GroupedRecentTaskInfo>> + consumer.accept(tasks.toList()) + } + } + + private fun createRecentTask(taskId: Int): RecentTask = + RecentTask( + taskId = taskId, + userId = 0, + topActivityComponent = null, + baseIntentComponent = null, + colorBackground = null + ) + + private fun createSingleTask(taskId: Int): GroupedRecentTaskInfo = + GroupedRecentTaskInfo.forSingleTask(createTaskInfo(taskId)) + + private fun createTaskPair(taskId1: Int, taskId2: Int): GroupedRecentTaskInfo = + GroupedRecentTaskInfo.forSplitTasks(createTaskInfo(taskId1), createTaskInfo(taskId2), null) + + private fun createTaskInfo(taskId: Int) = RecentTaskInfo().apply { this.taskId = taskId } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java index ecc845720717..cbe118635e95 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -226,7 +226,9 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { + " Tile records:\n" + " " + mockTileString + "\n" + " " + mockTileViewString + "\n" - + " media bounds: null\n"; + + " media bounds: null\n" + + " horizontal layout: false\n" + + " last orientation: 0\n"; assertEquals(expected, w.getBuffer().toString()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt index 39f27d43ed12..4af5b9018d5a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt @@ -123,8 +123,7 @@ class QuickQSPanelControllerTest : SysuiTestCase() { @Test fun mediaExpansion_afterConfigChange_inLandscape_collapsedInLandscapeTrue_updatesToCollapsed() { - // times(2) because both controller and base controller are registering their listeners - verify(quickQSPanel, times(2)).addOnConfigurationChangedListener(captor.capture()) + verify(quickQSPanel).addOnConfigurationChangedListener(captor.capture()) // verify that media starts in the expanded state by default verify(mediaHost).expansion = MediaHostState.EXPANDED @@ -139,8 +138,7 @@ class QuickQSPanelControllerTest : SysuiTestCase() { @Test fun mediaExpansion_afterConfigChange_landscape_collapsedInLandscapeFalse_remainsExpanded() { - // times(2) because both controller and base controller are registering their listeners - verify(quickQSPanel, times(2)).addOnConfigurationChangedListener(captor.capture()) + verify(quickQSPanel).addOnConfigurationChangedListener(captor.capture()) reset(mediaHost) usingCollapsedLandscapeMedia = false diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 2adc389a964e..481e4e9992d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -21,12 +21,16 @@ import android.testing.TestableLooper.RunWithLooper import android.view.MotionEvent import android.view.ViewGroup import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardHostViewController import com.android.keyguard.LockIconViewController +import com.android.keyguard.dagger.KeyguardBouncerComponent import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.dock.DockManager +import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.KeyguardUnlockAnimationController +import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.NotificationShadeDepthController @@ -51,9 +55,9 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +@SmallTest @RunWith(AndroidTestingRunner::class) @RunWithLooper(setAsMainLooper = true) -@SmallTest class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock private lateinit var view: NotificationShadeWindowView @@ -72,8 +76,12 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController @Mock + private lateinit var featureFlags: FeatureFlags + @Mock private lateinit var ambientState: AmbientState @Mock + private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel + @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager @@ -87,6 +95,10 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController @Mock private lateinit var pulsingGestureListener: PulsingGestureListener + @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory + @Mock lateinit var keyguardBouncerContainer: ViewGroup + @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent + @Mock lateinit var keyguardHostViewController: KeyguardHostViewController private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler> private lateinit var interactionEventHandler: InteractionEventHandler @@ -97,7 +109,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) whenever(view.bottom).thenReturn(VIEW_BOTTOM) - underTest = NotificationShadeWindowViewController( lockscreenShadeTransitionController, FalsingCollectorFake(), @@ -115,7 +126,10 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { notificationShadeWindowController, keyguardUnlockAnimationController, ambientState, - pulsingGestureListener + pulsingGestureListener, + featureFlags, + keyguardBouncerViewModel, + keyguardBouncerComponentFactory ) underTest.setupExpandedStatusBar() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java index 001bfeeabe6f..4a7dec912895 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java @@ -33,11 +33,14 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; import com.android.keyguard.LockIconViewController; +import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.dock.DockManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationShadeDepthController; @@ -86,6 +89,9 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; @Mock private AmbientState mAmbientState; @Mock private PulsingGestureListener mPulsingGestureListener; + @Mock private FeatureFlags mFeatureFlags; + @Mock private KeyguardBouncerViewModel mKeyguardBouncerViewModel; + @Mock private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory; @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler> mInteractionEventHandlerCaptor; @@ -121,7 +127,10 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { mNotificationShadeWindowController, mKeyguardUnlockAnimationController, mAmbientState, - mPulsingGestureListener + mPulsingGestureListener, + mFeatureFlags, + mKeyguardBouncerViewModel, + mKeyguardBouncerComponentFactory ); mController.setupExpandedStatusBar(); mController.setDragDownHelper(mDragDownHelper); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index 131eac668af3..8be138a3b2be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -113,7 +113,7 @@ class ClockRegistryTest : SysuiTestCase() { registry.isEnabled = true verify(mockPluginManager) - .addPluginListener(captor.capture(), eq(ClockProviderPlugin::class.java)) + .addPluginListener(captor.capture(), eq(ClockProviderPlugin::class.java), eq(true)) pluginListener = captor.value } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 945cf7f8774f..9a13e93ebcfc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -635,6 +635,19 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } @Test + public void transientIndication_visibleWhenDozing_ignoresPowerPressed() { + createController(); + + mController.setVisible(true); + reset(mRotateTextViewController); + mController.getKeyguardCallback().onBiometricError( + FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED, "foo", + BiometricSourceType.FINGERPRINT); + + verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE); + } + + @Test public void transientIndication_swipeUpToRetry() { createController(); String message = mContext.getString(R.string.keyguard_retry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index a4453f854ce5..ee4b9d9c93f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -41,11 +41,17 @@ import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardMessageArea; import com.android.keyguard.KeyguardMessageAreaController; +import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.keyguard.data.BouncerView; +import com.android.systemui.keyguard.data.BouncerViewDelegate; +import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor; +import com.android.systemui.keyguard.domain.interactor.BouncerInteractor; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.shade.NotificationPanelViewController; @@ -101,6 +107,13 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Mock private SysUIUnfoldComponent mSysUiUnfoldComponent; @Mock private DreamOverlayStateController mDreamOverlayStateController; @Mock private LatencyTracker mLatencyTracker; + @Mock private FeatureFlags mFeatureFlags; + @Mock private KeyguardSecurityModel mKeyguardSecurityModel; + @Mock private BouncerCallbackInteractor mBouncerCallbackInteractor; + @Mock private BouncerInteractor mBouncerInteractor; + @Mock private BouncerView mBouncerView; +// @Mock private WeakReference<BouncerViewDelegate> mBouncerViewDelegateWeakReference; + @Mock private BouncerViewDelegate mBouncerViewDelegate; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback; @@ -115,6 +128,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea); when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class))) .thenReturn(mKeyguardMessageAreaController); + when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate); + mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager( getContext(), @@ -133,7 +148,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mKeyguardMessageAreaFactory, Optional.of(mSysUiUnfoldComponent), () -> mShadeController, - mLatencyTracker); + mLatencyTracker, + mKeyguardSecurityModel, + mFeatureFlags, + mBouncerCallbackInteractor, + mBouncerInteractor, + mBouncerView); mStatusBarKeyguardViewManager.registerCentralSurfaces( mCentralSurfaces, mNotificationPanelView, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt index 37c0f3621b6f..bf432388ad28 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt @@ -34,14 +34,14 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper @SmallTest -class StatusBarUserSwitcherControllerTest : SysuiTestCase() { +class StatusBarUserSwitcherControllerOldImplTest : SysuiTestCase() { @Mock private lateinit var tracker: StatusBarUserInfoTracker diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt new file mode 100644 index 000000000000..f3046477f4d1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.statusbar.policy + +import android.content.pm.UserInfo +import android.graphics.Bitmap +import android.os.UserHandle +import android.view.View +import android.view.ViewGroup +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.qs.user.UserSwitchDialogController +import com.android.systemui.user.data.source.UserRecord +import com.android.systemui.util.mockito.kotlinArgumentCaptor +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import java.lang.ref.WeakReference +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(JUnit4::class) +class BaseUserSwitcherAdapterTest : SysuiTestCase() { + + @Mock private lateinit var controller: UserSwitcherController + + private lateinit var underTest: BaseUserSwitcherAdapter + + private lateinit var users: ArrayList<UserRecord> + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + users = + ArrayList( + listOf( + createUserRecord( + id = 0, + picture = mock(), + isSelected = true, + isGuest = false, + ), + createUserRecord( + id = 1, + picture = mock(), + isSelected = false, + isGuest = false, + ), + createUserRecord( + id = UserHandle.USER_NULL, + picture = null, + isSelected = false, + isGuest = true, + ), + ) + ) + + whenever(controller.users).thenAnswer { users } + + underTest = + object : BaseUserSwitcherAdapter(controller) { + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + return mock() + } + } + } + + @Test + fun `Adds self to controller in constructor`() { + val captor = kotlinArgumentCaptor<WeakReference<BaseUserSwitcherAdapter>>() + verify(controller).addAdapter(captor.capture()) + + assertThat(captor.value.get()).isEqualTo(underTest) + } + + @Test + fun count() { + assertThat(underTest.count).isEqualTo(users.size) + } + + @Test + fun `count - ignores restricted users when device is locked`() { + whenever(controller.isKeyguardShowing).thenReturn(true) + users = + ArrayList( + listOf( + createUserRecord( + id = 0, + picture = mock(), + isSelected = true, + isGuest = false, + isRestricted = false, + ), + createUserRecord( + id = 1, + picture = mock(), + isSelected = false, + isGuest = false, + isRestricted = true, // this one will be ignored. + ), + createUserRecord( + id = UserHandle.USER_NULL, + picture = null, + isSelected = false, + isGuest = true, + ), + ) + ) + assertThat(underTest.count).isEqualTo(users.size - 1) + } + + @Test + fun `count - does not ignore restricted users when device is not locked`() { + whenever(controller.isKeyguardShowing).thenReturn(false) + users = + ArrayList( + listOf( + createUserRecord( + id = 0, + picture = mock(), + isSelected = true, + isGuest = false, + isRestricted = false, + ), + createUserRecord( + id = 1, + picture = mock(), + isSelected = false, + isGuest = false, + isRestricted = true, + ), + createUserRecord( + id = UserHandle.USER_NULL, + picture = null, + isSelected = false, + isGuest = true, + ), + ) + ) + assertThat(underTest.count).isEqualTo(users.size) + } + + @Test + fun getItem() { + assertThat((0 until underTest.count).map { position -> underTest.getItem(position) }) + .isEqualTo(users) + } + + @Test + fun getItemId() { + (0 until underTest.count).map { position -> + assertThat(underTest.getItemId(position)).isEqualTo(position) + } + } + + @Test + fun onUserListItemClicked() { + val userRecord = users[users.size / 2] + val dialogShower: UserSwitchDialogController.DialogShower = mock() + + underTest.onUserListItemClicked(userRecord, dialogShower) + + verify(controller).onUserListItemClicked(userRecord, dialogShower) + } + + @Test + fun `getName - non guest - returns real name`() { + val userRecord = + createUserRecord( + id = 1, + picture = mock(), + ) + + assertThat(underTest.getName(context, userRecord)).isEqualTo(userRecord.info?.name) + } + + @Test + fun `getName - guest and selected - returns exit guest action name`() { + val expected = "Exit guest" + context.orCreateTestableResources.addOverride( + com.android.settingslib.R.string.guest_exit_quick_settings_button, + expected, + ) + + val userRecord = + createUserRecord( + id = 2, + picture = null, + isGuest = true, + isSelected = true, + ) + + assertThat(underTest.getName(context, userRecord)).isEqualTo(expected) + } + + @Test + fun `getName - guest and not selected - returns enter guest action name`() { + val expected = "Guest" + context.orCreateTestableResources.addOverride( + com.android.internal.R.string.guest_name, + expected, + ) + + val userRecord = + createUserRecord( + id = 2, + picture = null, + isGuest = true, + isSelected = false, + ) + + assertThat(underTest.getName(context, userRecord)).isEqualTo("Guest") + } + + @Test + fun refresh() { + underTest.refresh() + + verify(controller).refreshUsers(UserHandle.USER_NULL) + } + + private fun createUserRecord( + id: Int, + picture: Bitmap? = null, + isSelected: Boolean = false, + isGuest: Boolean = false, + isAction: Boolean = false, + isRestricted: Boolean = false, + ): UserRecord { + return UserRecord( + info = + if (isAction) { + null + } else { + UserInfo(id, "name$id", 0) + }, + picture = picture, + isCurrent = isSelected, + isGuest = isGuest, + isRestricted = isRestricted, + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java index fda80a2f9ed8..43d0fe9559b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java @@ -42,6 +42,7 @@ import com.android.settingslib.fuelgauge.BatterySaverUtils; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.dump.DumpManager; import com.android.systemui.power.EnhancedEstimates; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; @@ -80,6 +81,7 @@ public class BatteryControllerTest extends SysuiTestCase { mPowerManager, mBroadcastDispatcher, mDemoModeController, + mock(DumpManager.class), new Handler(), new Handler()); // Can throw if updateEstimate is called on the main thread diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt index b4f3987b2f95..b86ca6fc5375 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt @@ -38,9 +38,9 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @@ -102,8 +102,7 @@ class KeyguardQsUserSwitchControllerTest : SysuiTestCase() { ViewUtils.attachView(view) testableLooper.processAllMessages() - `when`(userSwitcherController.keyguardStateController).thenReturn(keyguardStateController) - `when`(userSwitcherController.keyguardStateController.isShowing).thenReturn(true) + `when`(userSwitcherController.isKeyguardShowing).thenReturn(true) `when`(keyguardStateController.isShowing).thenReturn(true) `when`(keyguardStateController.isKeyguardGoingAway).thenReturn(false) keyguardQsUserSwitchController.init() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt index 8dcd4bb3b738..76ecc1c7f36d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt @@ -86,7 +86,7 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest -class UserSwitcherControllerTest : SysuiTestCase() { +class UserSwitcherControllerOldImplTest : SysuiTestCase() { @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var activityManager: IActivityManager @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController @@ -118,7 +118,7 @@ class UserSwitcherControllerTest : SysuiTestCase() { private lateinit var longRunningExecutor: FakeExecutor private lateinit var uiExecutor: FakeExecutor private lateinit var uiEventLogger: UiEventLoggerFake - private lateinit var userSwitcherController: UserSwitcherController + private lateinit var userSwitcherController: UserSwitcherControllerOldImpl private lateinit var picture: Bitmap private val ownerId = UserHandle.USER_SYSTEM private val ownerInfo = UserInfo(ownerId, "Owner", null, @@ -205,7 +205,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { } private fun setupController() { - userSwitcherController = UserSwitcherController( + userSwitcherController = + UserSwitcherControllerOldImpl( mContext, activityManager, userManager, @@ -230,7 +231,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { dumpManager, dialogLaunchAnimator, guestResumeSessionReceiver, - guestResetOrExitSessionReceiver) + guestResetOrExitSessionReceiver + ) userSwitcherController.init(notificationShadeWindowView) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt index 6b466e1ac2d8..6fec343d036c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt @@ -60,7 +60,7 @@ class UserRepositoryImplTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - whenever(controller.addUsersFromLockScreen).thenReturn(MutableStateFlow(false)) + whenever(controller.isAddUsersFromLockScreenEnabled).thenReturn(MutableStateFlow(false)) whenever(controller.isGuestUserAutoCreated).thenReturn(false) whenever(controller.isGuestUserResetting).thenReturn(false) diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java index e47acd8d3834..343437634b29 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java @@ -41,6 +41,7 @@ import android.view.DisplayInfo; import android.view.SurfaceHolder; import com.android.systemui.SysuiTestCase; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.wallpapers.gl.ImageWallpaperRenderer; import org.junit.Before; @@ -72,6 +73,8 @@ public class ImageWallpaperTest extends SysuiTestCase { private Bitmap mWallpaperBitmap; @Mock private Handler mHandler; + @Mock + private FeatureFlags mFeatureFlags; private CountDownLatch mEventCountdown; @@ -100,7 +103,7 @@ public class ImageWallpaperTest extends SysuiTestCase { } private ImageWallpaper createImageWallpaper() { - return new ImageWallpaper() { + return new ImageWallpaper(mFeatureFlags) { @Override public Engine onCreateEngine() { return new GLEngine(mHandler) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRendererTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRendererTest.java new file mode 100644 index 000000000000..93f4f8223955 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRendererTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.wallpapers.canvas; + +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.hamcrest.MockitoHamcrest.intThat; + +import android.graphics.Bitmap; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.DisplayInfo; +import android.view.SurfaceHolder; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class ImageCanvasWallpaperRendererTest extends SysuiTestCase { + + private static final int MOBILE_DISPLAY_WIDTH = 720; + private static final int MOBILE_DISPLAY_HEIGHT = 1600; + + @Mock + private SurfaceHolder mMockSurfaceHolder; + + @Mock + private DisplayInfo mMockDisplayInfo; + + @Mock + private Bitmap mMockBitmap; + + @Before + public void setUp() throws Exception { + allowTestableLooperAsMainThread(); + MockitoAnnotations.initMocks(this); + } + + private void setDimensions( + int bitmapWidth, int bitmapHeight, + int displayWidth, int displayHeight) { + when(mMockBitmap.getWidth()).thenReturn(bitmapWidth); + when(mMockBitmap.getHeight()).thenReturn(bitmapHeight); + mMockDisplayInfo.logicalWidth = displayWidth; + mMockDisplayInfo.logicalHeight = displayHeight; + } + + private void testMinDimensions( + int bitmapWidth, int bitmapHeight) { + + clearInvocations(mMockSurfaceHolder); + setDimensions(bitmapWidth, bitmapHeight, + ImageCanvasWallpaperRendererTest.MOBILE_DISPLAY_WIDTH, + ImageCanvasWallpaperRendererTest.MOBILE_DISPLAY_HEIGHT); + + ImageCanvasWallpaperRenderer renderer = + new ImageCanvasWallpaperRenderer(mMockSurfaceHolder); + renderer.drawFrame(mMockBitmap, true); + + verify(mMockSurfaceHolder, times(1)).setFixedSize( + intThat(greaterThanOrEqualTo(ImageCanvasWallpaperRenderer.MIN_SURFACE_WIDTH)), + intThat(greaterThanOrEqualTo(ImageCanvasWallpaperRenderer.MIN_SURFACE_HEIGHT))); + } + + @Test + public void testMinSurface() { + // test that the surface is always at least MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT + testMinDimensions(8, 8); + + testMinDimensions(100, 2000); + + testMinDimensions(200, 1); + } + + private void testZeroDimensions(int bitmapWidth, int bitmapHeight) { + + clearInvocations(mMockSurfaceHolder); + setDimensions(bitmapWidth, bitmapHeight, + ImageCanvasWallpaperRendererTest.MOBILE_DISPLAY_WIDTH, + ImageCanvasWallpaperRendererTest.MOBILE_DISPLAY_HEIGHT); + + ImageCanvasWallpaperRenderer renderer = + new ImageCanvasWallpaperRenderer(mMockSurfaceHolder); + ImageCanvasWallpaperRenderer spyRenderer = spy(renderer); + spyRenderer.drawFrame(mMockBitmap, true); + + verify(mMockSurfaceHolder, never()).setFixedSize(anyInt(), anyInt()); + verify(spyRenderer, never()).drawWallpaperWithCanvas(any()); + } + + @Test + public void testZeroBitmap() { + // test that updateSurfaceSize is not called with a bitmap of width 0 or height 0 + testZeroDimensions( + 0, 1 + ); + + testZeroDimensions(1, 0 + ); + + testZeroDimensions(0, 0 + ); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index da33fa62a9ab..cebe946a459c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -34,6 +34,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.tracing.ProtoTracer; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.floating.FloatingTasks; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEventCallback; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; @@ -74,16 +75,16 @@ public class WMShellTest extends SysuiTestCase { @Mock ProtoTracer mProtoTracer; @Mock UserTracker mUserTracker; @Mock ShellExecutor mSysUiMainExecutor; + @Mock FloatingTasks mFloatingTasks; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mWMShell = new WMShell(mContext, mShellInterface, Optional.of(mPip), - Optional.of(mSplitScreen), Optional.of(mOneHanded), mCommandQueue, - mConfigurationController, mKeyguardStateController, mKeyguardUpdateMonitor, - mScreenLifecycle, mSysUiState, mProtoTracer, mWakefulnessLifecycle, - mUserTracker, mSysUiMainExecutor); + Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mFloatingTasks), + mCommandQueue, mConfigurationController, mKeyguardStateController, + mKeyguardUpdateMonitor, mScreenLifecycle, mSysUiState, mProtoTracer, + mWakefulnessLifecycle, mUserTracker, mSysUiMainExecutor); } @Test diff --git a/packages/VpnDialogs/res/values-ro/strings.xml b/packages/VpnDialogs/res/values-ro/strings.xml index 191736e0088f..f86a5d64fcce 100644 --- a/packages/VpnDialogs/res/values-ro/strings.xml +++ b/packages/VpnDialogs/res/values-ro/strings.xml @@ -17,8 +17,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="prompt" msgid="3183836924226407828">"Solicitare de conexiune"</string> - <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> dorește să configureze o conexiune VPN care să îi permită să monitorizeze traficul în rețea. Acceptați numai dacă aveți încredere în sursă. Atunci când conexiunea VPN este activă, <br /> <br /> <img src=vpn_icon /> se afișează în partea de sus a ecranului."</string> - <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> solicită permisiunea de a configura o conexiune VPN care să îi permită să monitorizeze traficul de rețea. Acceptați numai dacă aveți încredere în sursă. <br /> <br /> <img src=vpn_icon /> va apărea pe ecran atunci când conexiunea VPN este activă."</string> + <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> dorește să configureze o conexiune VPN care să îi permită să monitorizeze traficul în rețea. Acceptă numai dacă ai încredere în sursă. Când conexiunea VPN e activă, <br /> <br /> <img src=vpn_icon /> se afișează în partea de sus a ecranului."</string> + <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> solicită permisiunea de a configura o conexiune VPN care să îi permită să monitorizeze traficul de rețea. Acceptă numai dacă ai încredere în sursă. <br /> <br /> <img src=vpn_icon /> va apărea pe ecran când conexiunea VPN e activă."</string> <string name="legacy_title" msgid="192936250066580964">"VPN este conectat"</string> <string name="session" msgid="6470628549473641030">"Sesiune:"</string> <string name="duration" msgid="3584782459928719435">"Durată:"</string> @@ -26,12 +26,12 @@ <string name="data_received" msgid="4062776929376067820">"Primite:"</string> <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> byți/<xliff:g id="NUMBER_1">%2$s</xliff:g> pachete"</string> <string name="always_on_disconnected_title" msgid="1906740176262776166">"Nu se poate conecta la rețeaua VPN activată permanent"</string> - <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> este setată să rămână conectată permanent, dar momentan nu se poate conecta. Telefonul dvs. va folosi o rețea publică până când se va putea reconecta la <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string> - <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> este setată să rămână conectată permanent, dar momentan nu se poate conecta. Nu veți avea conexiune până când se va putea reconecta rețeaua VPN."</string> + <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> este setată să rămână conectată permanent, dar momentan nu se poate conecta. Telefonul va folosi o rețea publică până când se va putea reconecta la <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string> + <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> este setată să rămână conectată permanent, dar momentan nu se poate conecta. Nu vei avea conexiune până când se va putea reconecta rețeaua VPN."</string> <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string> - <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Modificați setările VPN"</string> + <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Modifică setările VPN"</string> <string name="configure" msgid="4905518375574791375">"Configurează"</string> <string name="disconnect" msgid="971412338304200056">"Deconectează"</string> - <string name="open_app" msgid="3717639178595958667">"Deschideți aplicația"</string> - <string name="dismiss" msgid="6192859333764711227">"Închideți"</string> + <string name="open_app" msgid="3717639178595958667">"Deschide aplicația"</string> + <string name="dismiss" msgid="6192859333764711227">"Închide"</string> </resources> diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml index 6e5947c0d753..b9cc0b066dcf 100644 --- a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml +++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Redați aplicațiile sub zona de decupaj"</string> + <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Redă aplicațiile sub zona de decupaj"</string> </resources> diff --git a/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml index e6281fd09b1a..2d7aaf6a46d2 100644 --- a/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml +++ b/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ascundeți"</string> + <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ascunde"</string> </resources> diff --git a/services/Android.bp b/services/Android.bp index 1e4ce19f1541..ccdf7ca06924 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -89,7 +89,6 @@ filegroup { ":services.autofill-sources", ":services.backup-sources", ":backuplib-sources", - ":services.cloudsearch-sources", ":services.companion-sources", ":services.contentcapture-sources", ":services.contentsuggestions-sources", @@ -102,7 +101,6 @@ filegroup { ":services.profcollect-sources", ":services.restrictions-sources", ":services.searchui-sources", - ":services.selectiontoolbar-sources", ":services.smartspace-sources", ":services.speech-sources", ":services.systemcaptions-sources", @@ -144,7 +142,6 @@ java_library { "services.appwidget", "services.autofill", "services.backup", - "services.cloudsearch", "services.companion", "services.contentcapture", "services.contentsuggestions", @@ -158,7 +155,6 @@ java_library { "services.profcollect", "services.restrictions", "services.searchui", - "services.selectiontoolbar", "services.smartspace", "services.speech", "services.systemcaptions", diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index fe85db286fa8..20b2a74f5be5 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -766,12 +766,14 @@ final class AutofillManagerServiceImpl /** * Updates the last fill selection when an authentication was selected. */ - void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState) { + void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState, + int uiType) { synchronized (mLock) { if (isValidEventLocked("setAuthenticationSelected()", sessionId)) { mEventHistory.addEvent( new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null, - null, null, null, null, null, null)); + null, null, null, null, null, null, + NO_SAVE_UI_REASON_NONE, uiType)); } } } @@ -780,12 +782,13 @@ final class AutofillManagerServiceImpl * Updates the last fill selection when an dataset authentication was selected. */ void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId, - @Nullable Bundle clientState) { + @Nullable Bundle clientState, int uiType) { synchronized (mLock) { if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) { mEventHistory.addEvent( new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset, - clientState, null, null, null, null, null, null, null, null)); + clientState, null, null, null, null, null, null, null, null, + NO_SAVE_UI_REASON_NONE, uiType)); } } } @@ -806,13 +809,13 @@ final class AutofillManagerServiceImpl * Updates the last fill response when a dataset was selected. */ void logDatasetSelected(@Nullable String selectedDataset, int sessionId, - @Nullable Bundle clientState, int presentationType) { + @Nullable Bundle clientState, int uiType) { synchronized (mLock) { if (isValidEventLocked("logDatasetSelected()", sessionId)) { mEventHistory.addEvent( new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null, null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE, - presentationType)); + uiType)); } } } @@ -820,13 +823,13 @@ final class AutofillManagerServiceImpl /** * Updates the last fill response when a dataset is shown. */ - void logDatasetShown(int sessionId, @Nullable Bundle clientState, int presentationType) { + void logDatasetShown(int sessionId, @Nullable Bundle clientState, int uiType) { synchronized (mLock) { if (isValidEventLocked("logDatasetShown", sessionId)) { mEventHistory.addEvent( new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE, - presentationType)); + uiType)); } } } diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java index b5fdaca838df..6bb19ce05813 100644 --- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java +++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java @@ -32,6 +32,8 @@ import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_ import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE; import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN; import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED; +import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_FILL_REQUEST_FAILED; +import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_NO_FOCUS; import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT; import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY; import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON; @@ -72,19 +74,32 @@ public final class PresentationStatsEventLogger { NOT_SHOWN_REASON_VIEW_CHANGED, NOT_SHOWN_REASON_ACTIVITY_FINISHED, NOT_SHOWN_REASON_REQUEST_TIMEOUT, + NOT_SHOWN_REASON_REQUEST_FAILED, + NOT_SHOWN_REASON_NO_FOCUS, NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY, NOT_SHOWN_REASON_UNKNOWN }) @Retention(RetentionPolicy.SOURCE) public @interface NotShownReason {} - public static final int NOT_SHOWN_REASON_ANY_SHOWN = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN; - public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED; - public static final int NOT_SHOWN_REASON_VIEW_CHANGED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED; - public static final int NOT_SHOWN_REASON_ACTIVITY_FINISHED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED; - public static final int NOT_SHOWN_REASON_REQUEST_TIMEOUT = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT; - public static final int NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY; - public static final int NOT_SHOWN_REASON_UNKNOWN = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON; + public static final int NOT_SHOWN_REASON_ANY_SHOWN = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN; + public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED; + public static final int NOT_SHOWN_REASON_VIEW_CHANGED = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED; + public static final int NOT_SHOWN_REASON_ACTIVITY_FINISHED = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED; + public static final int NOT_SHOWN_REASON_REQUEST_TIMEOUT = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT; + public static final int NOT_SHOWN_REASON_REQUEST_FAILED = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_FILL_REQUEST_FAILED; + public static final int NOT_SHOWN_REASON_NO_FOCUS = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_NO_FOCUS; + public static final int NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY; + public static final int NOT_SHOWN_REASON_UNKNOWN = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON; private final int mSessionId; private Optional<PresentationStatsEventInternal> mEventInternal; @@ -118,6 +133,14 @@ public final class PresentationStatsEventLogger { }); } + public void maybeSetNoPresentationEventReasonIfNoReasonExists(@NotShownReason int reason) { + mEventInternal.ifPresent(event -> { + if (event.mCountShown == 0 && event.mNoPresentationReason == NOT_SHOWN_REASON_UNKNOWN) { + event.mNoPresentationReason = reason; + } + }); + } + public void maybeSetAvailableCount(@Nullable List<Dataset> datasetList, AutofillId currentViewId) { mEventInternal.ifPresent(event -> { @@ -180,7 +203,8 @@ public final class PresentationStatsEventLogger { public void maybeSetInlinePresentationAndSuggestionHostUid(Context context, int userId) { mEventInternal.ifPresent(event -> { - event.mDisplayPresentationType = UI_TYPE_INLINE; + event.mDisplayPresentationType = + AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE; String imeString = Settings.Secure.getStringForUser(context.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD, userId); if (TextUtils.isEmpty(imeString)) { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index bd9c03a75f71..5c11e2c2327e 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -43,6 +43,8 @@ import static com.android.server.autofill.Helper.getNumericValue; import static com.android.server.autofill.Helper.sDebug; import static com.android.server.autofill.Helper.sVerbose; import static com.android.server.autofill.Helper.toArray; +import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_NO_FOCUS; +import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_FAILED; import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_TIMEOUT; import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_VIEW_CHANGED; import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED; @@ -402,6 +404,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private PresentationStatsEventLogger mPresentationStatsEventLogger; + /** + * Fill dialog request would likely be sent slightly later. + */ + @NonNull + @GuardedBy("mLock") + private boolean mStartedLogEventWithoutFocus; + void onSwitchInputMethodLocked() { // One caveat is that for the case where the focus is on a field for which regular autofill // returns null, and augmented autofill is triggered, and then the user switches the input @@ -522,6 +531,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } mLastFillRequest = mPendingFillRequest; + mPresentationStatsEventLogger.maybeSetIsNewRequest(true); mRemoteFillService.onFillRequest(mPendingFillRequest); mPendingInlineSuggestionsRequest = null; mWaitForInlineRequest = false; @@ -1128,6 +1138,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public void onFillRequestSuccess(int requestId, @Nullable FillResponse response, @NonNull String servicePackageName, int requestFlags) { + final AutofillId[] fieldClassificationIds; final LogMaker requestLog; @@ -1285,9 +1296,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - // TODO(b/234185326): Add separate reason for failures. mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( - NOT_SHOWN_REASON_REQUEST_TIMEOUT); + timedOut ? NOT_SHOWN_REASON_REQUEST_TIMEOUT : NOT_SHOWN_REASON_REQUEST_FAILED); mPresentationStatsEventLogger.logAndEndEvent(); } notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED, @@ -1396,7 +1406,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // AutoFillUiCallback @Override public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras, - boolean authenticateInline) { + int uiType) { if (sDebug) { Slog.d(TAG, "authenticate(): requestId=" + requestId + "; datasetIdx=" + datasetIndex + "; intentSender=" + intent); @@ -1415,12 +1425,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - mService.setAuthenticationSelected(id, mClientState); + mService.setAuthenticationSelected(id, mClientState, uiType); final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex); mHandler.sendMessage(obtainMessage( Session::startAuthentication, - this, authenticationId, intent, fillInIntent, authenticateInline)); + this, authenticationId, intent, fillInIntent, + /* authenticateInline= */ uiType == UI_TYPE_INLINE)); } // AutoFillUiCallback @@ -2815,6 +2826,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private boolean requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id, @NonNull ViewState viewState, int flags) { + // Force new response for manual request if ((flags & FLAG_MANUAL_REQUEST) != 0) { mSessionFlags.mAugmentedAutofillOnly = false; if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags); @@ -2822,7 +2834,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return true; } - // If it's not, then check if it it should start a partition. + // If it's not, then check if it should start a partition. if (shouldStartNewPartitionLocked(id)) { if (sDebug) { Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": " @@ -2921,7 +2933,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) { Slog.d(TAG, "Set the response has expired."); } - mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( + mPresentationStatsEventLogger.maybeSetNoPresentationEventReasonIfNoReasonExists( NOT_SHOWN_REASON_VIEW_CHANGED); mPresentationStatsEventLogger.logAndEndEvent(); return; @@ -2966,11 +2978,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // View is triggering autofill. mCurrentViewId = viewState.id; viewState.update(value, virtualBounds, flags); - if (!isRequestSupportFillDialog(flags)) { - mSessionFlags.mFillDialogDisabled = true; - } mPresentationStatsEventLogger.startNewEvent(); mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid()); + if (isRequestSupportFillDialog(flags)) { + // Set the default reason for now if the user doesn't trigger any focus event + // on the autofillable view. This can be changed downstream when more + // information is available or session is committed. + mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( + NOT_SHOWN_REASON_NO_FOCUS); + mStartedLogEventWithoutFocus = true; + } else { + mSessionFlags.mFillDialogDisabled = true; + mStartedLogEventWithoutFocus = false; + } requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags); break; case ACTION_VALUE_CHANGED: @@ -3013,6 +3033,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } break; case ACTION_VIEW_ENTERED: + boolean startedEventWithoutFocus = mStartedLogEventWithoutFocus; + mStartedLogEventWithoutFocus = false; if (sVerbose && virtualBounds != null) { Slog.v(TAG, "entered on virtual child " + id + ": " + virtualBounds); } @@ -3029,9 +3051,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( - NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED); - mPresentationStatsEventLogger.logAndEndEvent(); + // Previously, fill request will only start whenever a view is entered. + // With Fill Dialog, request starts prior to view getting entered. So, we can't end + // the event at this moment, otherwise we will be wrongly attributing fill dialog + // event as concluded. + if (!startedEventWithoutFocus) { + mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( + NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED); + mPresentationStatsEventLogger.logAndEndEvent(); + } if ((flags & FLAG_MANUAL_REQUEST) == 0) { // Not a manual request @@ -3059,9 +3087,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - mPresentationStatsEventLogger.startNewEvent(); - mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid()); + if (!startedEventWithoutFocus) { + mPresentationStatsEventLogger.startNewEvent(); + mPresentationStatsEventLogger.maybeSetAutofillServiceUid( + getAutofillServiceUid()); + } if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags)) { + // If a new request was issued even if previously it was fill dialog request, + // we should end the log event, and start a new one. However, it leaves us + // susceptible to race condition. But since mPresentationStatsEventLogger is + // lock guarded, we should be safe. + if (startedEventWithoutFocus) { + mPresentationStatsEventLogger.logAndEndEvent(); + mPresentationStatsEventLogger.startNewEvent(); + mPresentationStatsEventLogger.maybeSetAutofillServiceUid( + getAutofillServiceUid()); + } return; } @@ -3288,7 +3329,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (requestShowInlineSuggestionsLocked(response, filterText)) { final ViewState currentView = mViewStates.get(mCurrentViewId); currentView.setState(ViewState.STATE_INLINE_SHOWN); - // TODO(b/137800469): Fix it to log showed only when IME asks for inflation, + // TODO(b/248378401): Fix it to log showed only when IME asks for inflation, // rather than here where framework sends back the response. mService.logDatasetShown(id, mClientState, UI_TYPE_INLINE); @@ -3309,7 +3350,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState synchronized (mLock) { mService.logDatasetShown(id, mClientState, UI_TYPE_MENU); - mPresentationStatsEventLogger.maybeSetCountShown( response.getDatasets(), mCurrentViewId); mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_MENU); @@ -3455,7 +3495,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void authenticate(int requestId, int datasetIndex) { Session.this.authenticate(response.getRequestId(), datasetIndex, response.getAuthentication(), response.getClientState(), - /* authenticateInline= */ true); + UI_TYPE_INLINE); } @Override @@ -3995,7 +4035,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // ...or handle authentication. - mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState); + mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType); setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false); final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState); if (fillInIntent == null) { diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java index e07f41204500..5f0f9a3f1577 100644 --- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java @@ -84,7 +84,7 @@ public final class AutoFillUI { public interface AutoFillUiCallback { void authenticate(int requestId, int datasetIndex, @NonNull IntentSender intent, - @Nullable Bundle extras, boolean authenticateInline); + @Nullable Bundle extras, int uiType); void fill(int requestId, int datasetIndex, @NonNull Dataset dataset, @FillEventHistory.Event.UiType int uiType); void save(); @@ -232,7 +232,7 @@ public final class AutoFillUI { mCallback.authenticate(response.getRequestId(), AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED, response.getAuthentication(), response.getClientState(), - /* authenticateInline= */ false); + UI_TYPE_MENU); } } @@ -419,7 +419,7 @@ public final class AutoFillUI { mCallback.authenticate(response.getRequestId(), AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED, response.getAuthentication(), response.getClientState(), - /* authenticateInline= */ false); + UI_TYPE_DIALOG); } } diff --git a/services/cloudsearch/Android.bp b/services/cloudsearch/Android.bp deleted file mode 100644 index e38e6153016f..000000000000 --- a/services/cloudsearch/Android.bp +++ /dev/null @@ -1,22 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -filegroup { - name: "services.cloudsearch-sources", - srcs: ["java/**/*.java"], - path: "java", - visibility: ["//frameworks/base/services"], -} - -java_library_static { - name: "services.cloudsearch", - defaults: ["platform_service_defaults"], - srcs: [":services.cloudsearch-sources"], - libs: ["services.core"], -} diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java deleted file mode 100644 index ac2d1dd95da1..000000000000 --- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2022 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.server.cloudsearch; - -import static android.Manifest.permission.MANAGE_CLOUDSEARCH; -import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; -import static android.content.Context.CLOUDSEARCH_SERVICE; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UserIdInt; -import android.app.ActivityManagerInternal; -import android.app.cloudsearch.ICloudSearchManager; -import android.app.cloudsearch.ICloudSearchManagerCallback; -import android.app.cloudsearch.SearchRequest; -import android.app.cloudsearch.SearchResponse; -import android.content.Context; -import android.os.Binder; -import android.os.IBinder; -import android.os.ResultReceiver; -import android.os.ShellCallback; -import android.util.Slog; - -import com.android.internal.R; -import com.android.server.LocalServices; -import com.android.server.infra.AbstractMasterSystemService; -import com.android.server.infra.FrameworkResourcesServiceNameResolver; -import com.android.server.wm.ActivityTaskManagerInternal; - -import java.io.FileDescriptor; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -/** - * A service used to return cloudsearch targets given a query. - */ -public class CloudSearchManagerService extends - AbstractMasterSystemService<CloudSearchManagerService, CloudSearchPerUserService> { - - private static final String TAG = CloudSearchManagerService.class.getSimpleName(); - private static final boolean DEBUG = false; - - private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes - - private final ActivityTaskManagerInternal mActivityTaskManagerInternal; - - private final Context mContext; - - public CloudSearchManagerService(Context context) { - super(context, new FrameworkResourcesServiceNameResolver(context, - R.array.config_defaultCloudSearchServices, true), null, - PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH); - mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); - mContext = context; - } - - @Override - protected CloudSearchPerUserService newServiceLocked(int resolvedUserId, boolean disabled) { - return new CloudSearchPerUserService(this, mLock, resolvedUserId, ""); - } - - @Override - protected List<CloudSearchPerUserService> newServiceListLocked(int resolvedUserId, - boolean disabled, String[] serviceNames) { - if (serviceNames == null) { - return new ArrayList<>(); - } - List<CloudSearchPerUserService> serviceList = - new ArrayList<>(serviceNames.length); - for (int i = 0; i < serviceNames.length; i++) { - if (serviceNames[i] == null) { - continue; - } - serviceList.add(new CloudSearchPerUserService(this, mLock, resolvedUserId, - serviceNames[i])); - } - return serviceList; - } - - @Override - public void onStart() { - publishBinderService(CLOUDSEARCH_SERVICE, new CloudSearchManagerStub()); - } - - @Override - protected void enforceCallingPermissionForManagement() { - getContext().enforceCallingPermission(MANAGE_CLOUDSEARCH, TAG); - } - - @Override // from AbstractMasterSystemService - protected void onServicePackageUpdatedLocked(@UserIdInt int userId) { - final CloudSearchPerUserService service = peekServiceForUserLocked(userId); - if (service != null) { - service.onPackageUpdatedLocked(); - } - } - - @Override // from AbstractMasterSystemService - protected void onServicePackageRestartedLocked(@UserIdInt int userId) { - final CloudSearchPerUserService service = peekServiceForUserLocked(userId); - if (service != null) { - service.onPackageRestartedLocked(); - } - } - - @Override - protected int getMaximumTemporaryServiceDurationMs() { - return MAX_TEMP_SERVICE_DURATION_MS; - } - - private class CloudSearchManagerStub extends ICloudSearchManager.Stub { - - @Override - public void search(@NonNull SearchRequest searchRequest, - @NonNull ICloudSearchManagerCallback callBack) { - searchRequest.setCallerPackageName( - mContext.getPackageManager().getNameForUid(Binder.getCallingUid())); - runForUser("search", (service) -> { - synchronized (service.mLock) { - service.onSearchLocked(searchRequest, callBack); - } - }); - } - - @Override - public void returnResults(IBinder token, String requestId, SearchResponse response) { - runForUser("returnResults", (service) -> { - synchronized (service.mLock) { - service.onReturnResultsLocked(token, requestId, response); - } - }); - } - - public void destroy(@NonNull SearchRequest searchRequest) { - runForUser("destroyCloudSearchSession", (service) -> { - synchronized (service.mLock) { - service.onDestroyLocked(searchRequest.getRequestId()); - } - }); - } - - public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, - @Nullable FileDescriptor err, - @NonNull String[] args, @Nullable ShellCallback callback, - @NonNull ResultReceiver resultReceiver) { - new CloudSearchManagerServiceShellCommand(CloudSearchManagerService.this) - .exec(this, in, out, err, args, callback, resultReceiver); - } - - private void runForUser(@NonNull final String func, - @NonNull final Consumer<CloudSearchPerUserService> c) { - ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class); - final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), - Binder.getCallingUserHandle().getIdentifier(), false, ALLOW_NON_FULL, - null, null); - - if (DEBUG) { - Slog.d(TAG, "runForUser:" + func + " from pid=" + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - } - Context ctx = getContext(); - if (!(ctx.checkCallingPermission(MANAGE_CLOUDSEARCH) == PERMISSION_GRANTED - || mServiceNameResolver.isTemporary(userId) - || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) { - - String msg = "Permission Denial: Cannot call " + func + " from pid=" - + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid(); - Slog.w(TAG, msg); - throw new SecurityException(msg); - } - - final long origId = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - final List<CloudSearchPerUserService> services = - getServiceListForUserLocked(userId); - for (int i = 0; i < services.size(); i++) { - c.accept(services.get(i)); - } - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } -} diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java deleted file mode 100644 index c64982d94d6d..000000000000 --- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2022 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.server.cloudsearch; - -import android.annotation.NonNull; -import android.os.ShellCommand; - -import java.io.PrintWriter; - -/** - * The shell command implementation for the CloudSearchManagerService. - */ -public class CloudSearchManagerServiceShellCommand extends ShellCommand { - - private static final String TAG = - CloudSearchManagerServiceShellCommand.class.getSimpleName(); - - private final CloudSearchManagerService mService; - - public CloudSearchManagerServiceShellCommand(@NonNull CloudSearchManagerService service) { - mService = service; - } - - @Override - public int onCommand(String cmd) { - if (cmd == null) { - return handleDefaultCommands(cmd); - } - final PrintWriter pw = getOutPrintWriter(); - switch (cmd) { - case "set": { - final String what = getNextArgRequired(); - switch (what) { - case "temporary-service": { - final int userId = Integer.parseInt(getNextArgRequired()); - String serviceName = getNextArg(); - if (serviceName == null) { - mService.resetTemporaryService(userId); - pw.println("CloudSearchService temporarily reset. "); - return 0; - } - final int duration = Integer.parseInt(getNextArgRequired()); - String[] services = serviceName.split(";"); - if (services.length == 0) { - return 0; - } else { - mService.setTemporaryServices(userId, services, duration); - } - pw.println("CloudSearchService temporarily set to " + serviceName - + " for " + duration + "ms"); - break; - } - } - } - break; - default: - return handleDefaultCommands(cmd); - } - return 0; - } - - @Override - public void onHelp() { - try (PrintWriter pw = getOutPrintWriter()) { - pw.println("CloudSearchManagerService commands:"); - pw.println(" help"); - pw.println(" Prints this help text."); - pw.println(""); - pw.println(" set temporary-service USER_ID [COMPONENT_NAME DURATION]"); - pw.println(" Temporarily (for DURATION ms) changes the service implemtation."); - pw.println(" To reset, call with just the USER_ID argument."); - pw.println(""); - } - } -} diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java deleted file mode 100644 index 222d779f20e8..000000000000 --- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright (C) 2022 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.server.cloudsearch; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.AppGlobals; -import android.app.cloudsearch.ICloudSearchManagerCallback; -import android.app.cloudsearch.SearchRequest; -import android.app.cloudsearch.SearchResponse; -import android.content.ComponentName; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ServiceInfo; -import android.os.IBinder; -import android.os.RemoteException; -import android.service.cloudsearch.CloudSearchService; -import android.service.cloudsearch.ICloudSearchService; -import android.util.Slog; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.infra.AbstractRemoteService; -import com.android.server.CircularQueue; -import com.android.server.infra.AbstractPerUserSystemService; - -/** - * Per-user instance of {@link CloudSearchManagerService}. - */ -public class CloudSearchPerUserService extends - AbstractPerUserSystemService<CloudSearchPerUserService, CloudSearchManagerService> - implements RemoteCloudSearchService.RemoteCloudSearchServiceCallbacks { - - private static final String TAG = CloudSearchPerUserService.class.getSimpleName(); - private static final int QUEUE_SIZE = 10; - @GuardedBy("mLock") - private final CircularQueue<String, CloudSearchCallbackInfo> mCallbackQueue = - new CircularQueue<>(QUEUE_SIZE); - private final String mServiceName; - private final ComponentName mRemoteComponentName; - @Nullable - @GuardedBy("mLock") - private RemoteCloudSearchService mRemoteService; - /** - * When {@code true}, remote service died but service state is kept so it's restored after - * the system re-binds to it. - */ - @GuardedBy("mLock") - private boolean mZombie; - - protected CloudSearchPerUserService(CloudSearchManagerService master, - Object lock, int userId, String serviceName) { - super(master, lock, userId); - mServiceName = serviceName; - mRemoteComponentName = ComponentName.unflattenFromString(mServiceName); - } - - @Override // from PerUserSystemService - protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent) - throws NameNotFoundException { - - ServiceInfo si; - try { - si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, - PackageManager.GET_META_DATA, mUserId); - } catch (RemoteException e) { - throw new NameNotFoundException("Could not get service for " + serviceComponent); - } - // TODO(b/177858728): must check that either the service is from a system component, - // or it matches a service set by shell cmd (so it can be used on CTS tests and when - // OEMs are implementing the real service and also verify the proper permissions - return si; - } - - @GuardedBy("mLock") - @Override // from PerUserSystemService - protected boolean updateLocked(boolean disabled) { - final boolean enabledChanged = super.updateLocked(disabled); - if (enabledChanged) { - if (isEnabledLocked()) { - // Send the pending sessions over to the service - resurrectSessionsLocked(); - } else { - // Clear the remote service for the next call - updateRemoteServiceLocked(); - } - } - return enabledChanged; - } - - /** - * Notifies the service of a new cloudsearch session. - */ - @GuardedBy("mLock") - public void onSearchLocked(@NonNull SearchRequest searchRequest, - @NonNull ICloudSearchManagerCallback callback) { - if (mRemoteComponentName == null) { - return; - } - - String filterList = searchRequest.getSearchConstraints().containsKey( - SearchRequest.CONSTRAINT_SEARCH_PROVIDER_FILTER) - ? searchRequest.getSearchConstraints().getString( - SearchRequest.CONSTRAINT_SEARCH_PROVIDER_FILTER) : ""; - - String remoteServicePackageName = mRemoteComponentName.getPackageName(); - // By default, all providers are marked as wanted. - boolean wantedProvider = true; - if (filterList.length() > 0) { - // If providers are specified by the client, - wantedProvider = false; - String[] providersSpecified = filterList.split(";"); - for (int i = 0; i < providersSpecified.length; i++) { - if (providersSpecified[i].equals(remoteServicePackageName)) { - wantedProvider = true; - break; - } - } - } - // If the provider was not requested by the Client, the request will not be sent to the - // provider. - if (!wantedProvider) { - // TODO(216520546) Send a failure callback to the client. - return; - } - final boolean serviceExists = resolveService(searchRequest, - s -> s.onSearch(searchRequest)); - String requestId = searchRequest.getRequestId(); - if (serviceExists && !mCallbackQueue.containsKey(requestId)) { - final CloudSearchCallbackInfo sessionInfo = new CloudSearchCallbackInfo( - requestId, searchRequest, callback, callback.asBinder(), () -> { - synchronized (mLock) { - onDestroyLocked(requestId); - } - }); - if (sessionInfo.linkToDeath()) { - CloudSearchCallbackInfo removedInfo = mCallbackQueue.put(requestId, sessionInfo); - if (removedInfo != null) { - removedInfo.destroy(); - } - } else { - // destroy the session if calling process is already dead - onDestroyLocked(requestId); - } - } - } - - /** - * Used to return results back to the clients. - */ - @GuardedBy("mLock") - public void onReturnResultsLocked(@NonNull IBinder token, - @NonNull String requestId, - @NonNull SearchResponse response) { - if (mRemoteService == null) { - return; - } - ICloudSearchService serviceInterface = mRemoteService.getServiceInterface(); - if (serviceInterface == null || token != serviceInterface.asBinder()) { - return; - } - if (mCallbackQueue.containsKey(requestId)) { - response.setSource(mServiceName); - final CloudSearchCallbackInfo sessionInfo = mCallbackQueue.getElement(requestId); - try { - if (response.getStatusCode() == SearchResponse.SEARCH_STATUS_OK) { - sessionInfo.mCallback.onSearchSucceeded(response); - } else { - sessionInfo.mCallback.onSearchFailed(response); - } - } catch (RemoteException e) { - if (mMaster.debug) { - Slog.e(TAG, "Exception in posting results"); - e.printStackTrace(); - } - onDestroyLocked(requestId); - } - } - } - - /** - * Notifies the server about the end of an existing cloudsearch session. - */ - @GuardedBy("mLock") - public void onDestroyLocked(@NonNull String requestId) { - if (isDebug()) { - Slog.d(TAG, "onDestroyLocked(): requestId=" + requestId); - } - final CloudSearchCallbackInfo sessionInfo = mCallbackQueue.removeElement(requestId); - if (sessionInfo != null) { - sessionInfo.destroy(); - } - } - - @Override - public void onFailureOrTimeout(boolean timedOut) { - if (isDebug()) { - Slog.d(TAG, "onFailureOrTimeout(): timed out=" + timedOut); - } - // Do nothing, we are just proxying to the cloudsearch service - } - - @Override - public void onConnectedStateChanged(boolean connected) { - if (isDebug()) { - Slog.d(TAG, "onConnectedStateChanged(): connected=" + connected); - } - if (connected) { - synchronized (mLock) { - if (mZombie) { - // Validation check - shouldn't happen - if (mRemoteService == null) { - Slog.w(TAG, "Cannot resurrect sessions because remote service is null"); - return; - } - mZombie = false; - resurrectSessionsLocked(); - } - } - } - } - - @Override - public void onServiceDied(RemoteCloudSearchService service) { - if (isDebug()) { - Slog.w(TAG, "onServiceDied(): service=" + service); - } - synchronized (mLock) { - mZombie = true; - } - updateRemoteServiceLocked(); - } - - @GuardedBy("mLock") - private void updateRemoteServiceLocked() { - if (mRemoteService != null) { - mRemoteService.destroy(); - mRemoteService = null; - } - } - - void onPackageUpdatedLocked() { - if (isDebug()) { - Slog.v(TAG, "onPackageUpdatedLocked()"); - } - destroyAndRebindRemoteService(); - } - - void onPackageRestartedLocked() { - if (isDebug()) { - Slog.v(TAG, "onPackageRestartedLocked()"); - } - destroyAndRebindRemoteService(); - } - - private void destroyAndRebindRemoteService() { - if (mRemoteService == null) { - return; - } - - if (isDebug()) { - Slog.d(TAG, "Destroying the old remote service."); - } - mRemoteService.destroy(); - mRemoteService = null; - - synchronized (mLock) { - mZombie = true; - } - mRemoteService = getRemoteServiceLocked(); - if (mRemoteService != null) { - if (isDebug()) { - Slog.d(TAG, "Rebinding to the new remote service."); - } - mRemoteService.reconnect(); - } - } - - /** - * Called after the remote service connected, it's used to restore state from a 'zombie' - * service (i.e., after it died). - */ - private void resurrectSessionsLocked() { - final int numCallbacks = mCallbackQueue.size(); - if (isDebug()) { - Slog.d(TAG, "Resurrecting remote service (" + mRemoteService + ") on " - + numCallbacks + " requests."); - } - - for (CloudSearchCallbackInfo callbackInfo : mCallbackQueue.values()) { - callbackInfo.resurrectSessionLocked(this, callbackInfo.mToken); - } - } - - @GuardedBy("mLock") - @Nullable - protected boolean resolveService( - @NonNull final SearchRequest requestId, - @NonNull final AbstractRemoteService.AsyncRequest<ICloudSearchService> cb) { - - final RemoteCloudSearchService service = getRemoteServiceLocked(); - if (service != null) { - service.executeOnResolvedService(cb); - } - return service != null; - } - - @GuardedBy("mLock") - @Nullable - private RemoteCloudSearchService getRemoteServiceLocked() { - if (mRemoteService == null) { - final String serviceName = getComponentNameForMultipleLocked(mServiceName); - if (serviceName == null) { - if (mMaster.verbose) { - Slog.v(TAG, "getRemoteServiceLocked(): not set"); - } - return null; - } - ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName); - - mRemoteService = new RemoteCloudSearchService(getContext(), - CloudSearchService.SERVICE_INTERFACE, serviceComponent, mUserId, this, - mMaster.isBindInstantServiceAllowed(), mMaster.verbose); - } - - return mRemoteService; - } - - private static final class CloudSearchCallbackInfo { - private static final boolean DEBUG = false; // Do not submit with true - @NonNull - final IBinder mToken; - @NonNull - final IBinder.DeathRecipient mDeathRecipient; - @NonNull - private final String mRequestId; - @NonNull - private final SearchRequest mSearchRequest; - private final ICloudSearchManagerCallback mCallback; - - CloudSearchCallbackInfo( - @NonNull final String id, - @NonNull final SearchRequest request, - @NonNull final ICloudSearchManagerCallback callback, - @NonNull final IBinder token, - @NonNull final IBinder.DeathRecipient deathRecipient) { - if (DEBUG) { - Slog.d(TAG, "Creating CloudSearchSessionInfo for session Id=" + id); - } - mRequestId = id; - mSearchRequest = request; - mCallback = callback; - mToken = token; - mDeathRecipient = deathRecipient; - } - - boolean linkToDeath() { - try { - mToken.linkToDeath(mDeathRecipient, 0); - } catch (RemoteException e) { - if (DEBUG) { - Slog.w(TAG, "Caller is dead before session can be started, requestId: " - + mRequestId); - } - return false; - } - return true; - } - - void destroy() { - if (DEBUG) { - Slog.d(TAG, "Removing callback for Request Id=" + mRequestId); - } - if (mToken != null) { - mToken.unlinkToDeath(mDeathRecipient, 0); - } - mCallback.asBinder().unlinkToDeath(mDeathRecipient, 0); - } - - void resurrectSessionLocked(CloudSearchPerUserService service, IBinder token) { - if (DEBUG) { - Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked() - + ") for request Id=" + mRequestId); - } - service.onSearchLocked(mSearchRequest, mCallback); - } - } -} diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java b/services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java deleted file mode 100644 index d1c0482b4be6..000000000000 --- a/services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2022 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.server.cloudsearch; - -import android.annotation.NonNull; -import android.content.ComponentName; -import android.content.Context; -import android.os.IBinder; -import android.service.cloudsearch.ICloudSearchService; -import android.text.format.DateUtils; - -import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; - - -/** - * Proxy to the {@link android.service.cloudsearch.CloudSearchService} implementation in another - * process. - */ -public class RemoteCloudSearchService extends - AbstractMultiplePendingRequestsRemoteService<RemoteCloudSearchService, - ICloudSearchService> { - - private static final String TAG = "RemoteCloudSearchService"; - - private static final long TIMEOUT_IDLE_BOUND_TIMEOUT_MS = 10 * DateUtils.MINUTE_IN_MILLIS; - private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS; - - private final RemoteCloudSearchServiceCallbacks mCallback; - - public RemoteCloudSearchService(Context context, String serviceInterface, - ComponentName componentName, int userId, - RemoteCloudSearchServiceCallbacks callback, boolean bindInstantServiceAllowed, - boolean verbose) { - super(context, serviceInterface, componentName, userId, callback, - context.getMainThreadHandler(), - bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, - verbose, /* initialCapacity= */ 1); - mCallback = callback; - } - - @Override - protected ICloudSearchService getServiceInterface(IBinder service) { - return ICloudSearchService.Stub.asInterface(service); - } - - @Override - protected long getTimeoutIdleBindMillis() { - return TIMEOUT_IDLE_BOUND_TIMEOUT_MS; - } - - @Override - protected long getRemoteRequestMillis() { - return TIMEOUT_REMOTE_REQUEST_MILLIS; - } - - /** - * Schedules a request to bind to the remote service. - */ - public void reconnect() { - super.scheduleBind(); - } - - /** - * Schedule async request on remote service. - */ - public void scheduleOnResolvedService(@NonNull AsyncRequest<ICloudSearchService> request) { - scheduleAsyncRequest(request); - } - - /** - * Execute async request on remote service immediately instead of sending it to Handler queue. - */ - public void executeOnResolvedService(@NonNull AsyncRequest<ICloudSearchService> request) { - executeAsyncRequest(request); - } - - /** - * Failure callback - */ - public interface RemoteCloudSearchServiceCallbacks - extends VultureCallback<RemoteCloudSearchService> { - - /** - * Notifies a the failure or timeout of a remote call. - */ - void onFailureOrTimeout(boolean timedOut); - - /** - * Notifies change in connected state of the remote service. - */ - void onConnectedStateChanged(boolean connected); - } - - @Override // from AbstractRemoteService - protected void handleOnConnectedStateChanged(boolean connected) { - if (mCallback != null) { - mCallback.onConnectedStateChanged(connected); - } - } -} diff --git a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java index ec0da490adcf..2904f28fca01 100644 --- a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java +++ b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java @@ -22,14 +22,19 @@ import android.annotation.NonNull; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraInjectionSession; import android.hardware.camera2.CameraManager; +import android.os.Process; +import android.os.UserManager; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import java.util.List; import java.util.Set; /** @@ -45,6 +50,7 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen private final CameraAccessBlockedCallback mBlockedCallback; private final CameraManager mCameraManager; private final PackageManager mPackageManager; + private final UserManager mUserManager; @GuardedBy("mLock") private int mObserverCount = 0; @@ -66,7 +72,7 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen static class OpenCameraInfo { public String packageName; - public int packageUid; + public Set<Integer> packageUids; } interface CameraAccessBlockedCallback { @@ -85,6 +91,7 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen mBlockedCallback = blockedCallback; mCameraManager = mContext.getSystemService(CameraManager.class); mPackageManager = mContext.getPackageManager(); + mUserManager = mContext.getSystemService(UserManager.class); } /** @@ -125,16 +132,18 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen for (int i = 0; i < mAppsToBlockOnVirtualDevice.size(); i++) { final String cameraId = mAppsToBlockOnVirtualDevice.keyAt(i); final OpenCameraInfo openCameraInfo = mAppsToBlockOnVirtualDevice.get(cameraId); - int packageUid = openCameraInfo.packageUid; - if (runningUids.contains(packageUid)) { - final String packageName = openCameraInfo.packageName; - InjectionSessionData data = mPackageToSessionData.get(packageName); - if (data == null) { - data = new InjectionSessionData(); - data.appUid = packageUid; - mPackageToSessionData.put(packageName, data); + final String packageName = openCameraInfo.packageName; + for (int packageUid : openCameraInfo.packageUids) { + if (runningUids.contains(packageUid)) { + InjectionSessionData data = mPackageToSessionData.get(packageName); + if (data == null) { + data = new InjectionSessionData(); + data.appUid = packageUid; + mPackageToSessionData.put(packageName, data); + } + startBlocking(packageName, cameraId); + break; } - startBlocking(packageName, cameraId); } } } @@ -155,37 +164,41 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen @Override public void onCameraOpened(@NonNull String cameraId, @NonNull String packageName) { synchronized (mLock) { - try { - final ApplicationInfo ainfo = mPackageManager.getApplicationInfo(packageName, 0); - InjectionSessionData data = mPackageToSessionData.get(packageName); - if (!mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(ainfo.uid)) { - OpenCameraInfo openCameraInfo = new OpenCameraInfo(); - openCameraInfo.packageName = packageName; - openCameraInfo.packageUid = ainfo.uid; - mAppsToBlockOnVirtualDevice.put(cameraId, openCameraInfo); - CameraInjectionSession existingSession = - (data != null) ? data.cameraIdToSession.get(cameraId) : null; - if (existingSession != null) { - existingSession.close(); - data.cameraIdToSession.remove(cameraId); - if (data.cameraIdToSession.isEmpty()) { - mPackageToSessionData.remove(packageName); - } + InjectionSessionData data = mPackageToSessionData.get(packageName); + List<UserInfo> aliveUsers = mUserManager.getAliveUsers(); + ArraySet<Integer> packageUids = new ArraySet<>(); + for (UserInfo user : aliveUsers) { + int userId = user.getUserHandle().getIdentifier(); + int appUid = queryUidFromPackageName(userId, packageName); + if (mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(appUid)) { + if (data == null) { + data = new InjectionSessionData(); + data.appUid = appUid; + mPackageToSessionData.put(packageName, data); + } + if (data.cameraIdToSession.containsKey(cameraId)) { + return; } + startBlocking(packageName, cameraId); return; + } else { + if (appUid != Process.INVALID_UID) { + packageUids.add(appUid); + } } - if (data == null) { - data = new InjectionSessionData(); - data.appUid = ainfo.uid; - mPackageToSessionData.put(packageName, data); - } - if (data.cameraIdToSession.containsKey(cameraId)) { - return; + } + OpenCameraInfo openCameraInfo = new OpenCameraInfo(); + openCameraInfo.packageName = packageName; + openCameraInfo.packageUids = packageUids; + mAppsToBlockOnVirtualDevice.put(cameraId, openCameraInfo); + CameraInjectionSession existingSession = + (data != null) ? data.cameraIdToSession.get(cameraId) : null; + if (existingSession != null) { + existingSession.close(); + data.cameraIdToSession.remove(cameraId); + if (data.cameraIdToSession.isEmpty()) { + mPackageToSessionData.remove(packageName); } - startBlocking(packageName, cameraId); - } catch (PackageManager.NameNotFoundException e) { - Slog.e(TAG, "onCameraOpened - unknown package " + packageName, e); - return; } } } @@ -274,4 +287,16 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen } } } + + private int queryUidFromPackageName(int userId, String packageName) { + try { + final ApplicationInfo ainfo = + mPackageManager.getApplicationInfoAsUser(packageName, + PackageManager.GET_ACTIVITIES, userId); + return ainfo.uid; + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "queryUidFromPackageName - unknown package " + packageName, e); + return Process.INVALID_UID; + } + } } diff --git a/services/contentcapture/Android.bp b/services/contentcapture/Android.bp index 434f239dbddc..5392c2cde3b8 100644 --- a/services/contentcapture/Android.bp +++ b/services/contentcapture/Android.bp @@ -17,6 +17,9 @@ filegroup { java_library_static { name: "services.contentcapture", defaults: ["platform_service_defaults"], - srcs: [":services.contentcapture-sources"], + srcs: [ + ":services.contentcapture-sources", + "java/**/*.logtags", + ], libs: ["services.core"], } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 41a759254909..c503a5a89afd 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -60,6 +60,7 @@ import android.service.contentcapture.SnapshotData; import android.service.voice.VoiceInteractionManagerInternal; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.EventLog; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -69,6 +70,7 @@ import android.view.contentcapture.DataShareRequest; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; +import com.android.internal.util.CollectionUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks; @@ -88,6 +90,10 @@ final class ContentCapturePerUserService private static final String TAG = ContentCapturePerUserService.class.getSimpleName(); + private static final int EVENT_LOG_CONNECT_STATE_DIED = 0; + static final int EVENT_LOG_CONNECT_STATE_CONNECTED = 1; + static final int EVENT_LOG_CONNECT_STATE_DISCONNECTED = 2; + @GuardedBy("mLock") private final SparseArray<ContentCaptureServerSession> mSessions = new SparseArray<>(); @@ -190,9 +196,12 @@ final class ContentCapturePerUserService Slog.w(TAG, "remote service died: " + service); synchronized (mLock) { mZombie = true; + ComponentName serviceComponent = getServiceComponentName(); writeServiceEvent( FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_REMOTE_SERVICE_DIED, - getServiceComponentName()); + serviceComponent); + EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED, mUserId, + EVENT_LOG_CONNECT_STATE_DIED, 0); } } @@ -529,6 +538,15 @@ final class ContentCapturePerUserService return mConditionsByPkg.get(packageName); } + @Nullable + ArraySet<String> getContentCaptureAllowlist() { + ArraySet<String> allowPackages; + synchronized (mLock) { + allowPackages = mMaster.mGlobalContentCaptureOptions.getWhitelistedPackages(mUserId); + } + return allowPackages; + } + @GuardedBy("mLock") void onActivityEventLocked(@NonNull ComponentName componentName, @ActivityEventType int type) { if (mRemoteService == null) { @@ -617,8 +635,12 @@ final class ContentCapturePerUserService ArraySet<String> oldList = mMaster.mGlobalContentCaptureOptions.getWhitelistedPackages(mUserId); + EventLog.writeEvent(EventLogTags.CC_CURRENT_ALLOWLIST, mUserId, + CollectionUtils.size(oldList)); mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities); + EventLog.writeEvent(EventLogTags.CC_SET_ALLOWLIST, mUserId, + CollectionUtils.size(packages), CollectionUtils.size(activities)); writeSetWhitelistEvent(getServiceComponentName(), packages, activities); updateContentCaptureOptions(oldList); @@ -699,13 +721,15 @@ final class ContentCapturePerUserService private void updateContentCaptureOptions(@Nullable ArraySet<String> oldList) { ArraySet<String> adding = mMaster.mGlobalContentCaptureOptions .getWhitelistedPackages(mUserId); + int addingCount = CollectionUtils.size(adding); + EventLog.writeEvent(EventLogTags.CC_CURRENT_ALLOWLIST, mUserId, addingCount); if (oldList != null && adding != null) { adding.removeAll(oldList); } - int N = adding != null ? adding.size() : 0; - for (int i = 0; i < N; i++) { + EventLog.writeEvent(EventLogTags.CC_UPDATE_OPTIONS, mUserId, addingCount); + for (int i = 0; i < addingCount; i++) { String packageName = adding.valueAt(i); ContentCaptureOptions options = mMaster.mGlobalContentCaptureOptions .getOptions(mUserId, packageName); diff --git a/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags b/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags new file mode 100644 index 000000000000..5218b26397df --- /dev/null +++ b/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags @@ -0,0 +1,13 @@ +# See system/logging/logcat/event.logtags for a description of the format of this file. + +option java_package com.android.server.contentcapture + +# ContentCaptureService connection state change, refer to ContentCapturePerUserService +# for type definition +53200 cc_connect_state_changed (user|1|5),(type|1|5),(package_count|1|1) +# Set the package and activity allowlist +53201 cc_set_allowlist (user|1|5),(package_count|1|1),(activity_count|1|1) +# Get the current allowlist +53202 cc_current_allowlist (user|1|5),(count|1|1) +# update content capture client option with new allow list count +53203 cc_update_options (user|1|5),(count|1) diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java index 1efe55aa0767..3907de4d903a 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java @@ -31,6 +31,7 @@ import android.service.contentcapture.IContentCaptureService; import android.service.contentcapture.IContentCaptureServiceCallback; import android.service.contentcapture.IDataShareCallback; import android.service.contentcapture.SnapshotData; +import android.util.EventLog; import android.util.Slog; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.DataRemovalRequest; @@ -38,6 +39,7 @@ import android.view.contentcapture.DataShareRequest; import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; import com.android.internal.os.IResultReceiver; +import com.android.internal.util.CollectionUtils; import com.android.internal.util.FrameworkStatsLog; final class RemoteContentCaptureService @@ -88,6 +90,10 @@ final class RemoteContentCaptureService writeServiceEvent( FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_CONNECTED, mComponentName); + EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED, + mPerUserService.getUserId(), + ContentCapturePerUserService.EVENT_LOG_CONNECT_STATE_CONNECTED, + CollectionUtils.size(mPerUserService.getContentCaptureAllowlist())); } finally { // Update the system-service state, in case the service reconnected after // dying @@ -98,6 +104,9 @@ final class RemoteContentCaptureService writeServiceEvent( FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_DISCONNECTED, mComponentName); + EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED, + mPerUserService.getUserId(), + ContentCapturePerUserService.EVENT_LOG_CONNECT_STATE_DISCONNECTED, 0); } } catch (Exception e) { Slog.w(mTag, "Exception calling onConnectedStateChanged(" + connected + "): " + e); diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java index 61d85689f295..e97654cde174 100644 --- a/services/core/java/com/android/server/am/AppProfiler.java +++ b/services/core/java/com/android/server/am/AppProfiler.java @@ -44,6 +44,7 @@ import static com.android.server.am.ActivityManagerService.appendMemInfo; import static com.android.server.am.ActivityManagerService.getKsmInfo; import static com.android.server.am.ActivityManagerService.stringifyKBSize; import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING; +import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_NONE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; import static com.android.server.wm.ActivityTaskManagerService.DUMP_ACTIVITIES_CMD; @@ -1047,17 +1048,7 @@ public class AppProfiler { } trimMemoryUiHiddenIfNecessaryLSP(app); if (curProcState >= ActivityManager.PROCESS_STATE_HOME && !app.isKilledByAm()) { - if (trimMemoryLevel < curLevel[0] && (thread = app.getThread()) != null) { - try { - if (DEBUG_SWITCH || DEBUG_OOM_ADJ) { - Slog.v(TAG_OOM_ADJ, - "Trimming memory of " + app.processName - + " to " + curLevel[0]); - } - thread.scheduleTrimMemory(curLevel[0]); - } catch (RemoteException e) { - } - } + scheduleTrimMemoryLSP(app, curLevel[0], "Trimming memory of "); profile.setTrimMemoryLevel(curLevel[0]); step[0]++; if (step[0] >= actualFactor) { @@ -1073,31 +1064,11 @@ public class AppProfiler { } } else if (curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT && !app.isKilledByAm()) { - if (trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND - && (thread = app.getThread()) != null) { - try { - if (DEBUG_SWITCH || DEBUG_OOM_ADJ) { - Slog.v(TAG_OOM_ADJ, - "Trimming memory of heavy-weight " + app.processName - + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND); - } - thread.scheduleTrimMemory( - ComponentCallbacks2.TRIM_MEMORY_BACKGROUND); - } catch (RemoteException e) { - } - } + scheduleTrimMemoryLSP(app, ComponentCallbacks2.TRIM_MEMORY_BACKGROUND, + "Trimming memory of heavy-weight "); profile.setTrimMemoryLevel(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND); } else { - if (trimMemoryLevel < fgTrimLevel && (thread = app.getThread()) != null) { - try { - if (DEBUG_SWITCH || DEBUG_OOM_ADJ) { - Slog.v(TAG_OOM_ADJ, "Trimming memory of fg " + app.processName - + " to " + fgTrimLevel); - } - thread.scheduleTrimMemory(fgTrimLevel); - } catch (RemoteException e) { - } - } + scheduleTrimMemoryLSP(app, fgTrimLevel, "Trimming memory of fg "); profile.setTrimMemoryLevel(fgTrimLevel); } }); @@ -1128,19 +1099,25 @@ public class AppProfiler { // If this application is now in the background and it // had done UI, then give it the special trim level to // have it free UI resources. - final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN; - IApplicationThread thread; - if (app.mProfile.getTrimMemoryLevel() < level && (thread = app.getThread()) != null) { - try { - if (DEBUG_SWITCH || DEBUG_OOM_ADJ) { - Slog.v(TAG_OOM_ADJ, "Trimming memory of bg-ui " - + app.processName + " to " + level); - } - thread.scheduleTrimMemory(level); - } catch (RemoteException e) { + scheduleTrimMemoryLSP(app, ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN, + "Trimming memory of bg-ui "); + app.mProfile.setPendingUiClean(false); + } + } + + @GuardedBy({"mService", "mProcLock"}) + private void scheduleTrimMemoryLSP(ProcessRecord app, int level, String msg) { + IApplicationThread thread; + if (app.mProfile.getTrimMemoryLevel() < level && (thread = app.getThread()) != null) { + try { + if (DEBUG_SWITCH || DEBUG_OOM_ADJ) { + Slog.v(TAG_OOM_ADJ, msg + app.processName + " to " + level); } + mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app, + OOM_ADJ_REASON_NONE); + thread.scheduleTrimMemory(level); + } catch (RemoteException e) { } - app.mProfile.setPendingUiClean(false); } } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index a71f51a9c8b7..e31c952e10f9 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -46,6 +46,7 @@ import static android.app.AppOpsManager.OP_PLAY_AUDIO; import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD; +import static android.app.AppOpsManager.OP_VIBRATE; import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_FAILED; import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_RESUMED; import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_STARTED; @@ -266,6 +267,7 @@ public class AppOpsService extends IAppOpsService.Stub { OP_PLAY_AUDIO, OP_RECORD_AUDIO, OP_CAMERA, + OP_VIBRATE, }; private static final int MAX_UNFORWARDED_OPS = 10; diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 481c5db559cf..8510de4ef201 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -4182,8 +4182,6 @@ public class Vpn { */ @NonNull public synchronized List<String> getAppExclusionList(@NonNull String packageName) { - enforceNotRestrictedUser(); - final long oldId = Binder.clearCallingIdentity(); try { final byte[] bytes = getVpnProfileStore().get(getVpnAppExcludedForPackage(packageName)); diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index 8e00ccf68fb1..44c8e18a22cf 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -21,8 +21,11 @@ import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STA import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE; import static com.android.server.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS; +import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE; +import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE; import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE; import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED; +import static com.android.server.devicestate.OverrideRequestController.STATUS_UNKNOWN; import android.annotation.IntDef; import android.annotation.IntRange; @@ -106,6 +109,8 @@ public final class DeviceStateManagerService extends SystemService { private final BinderService mBinderService; @NonNull private final OverrideRequestController mOverrideRequestController; + @NonNull + private final DeviceStateProviderListener mDeviceStateProviderListener; @VisibleForTesting @NonNull public ActivityTaskManagerInternal mActivityTaskManagerInternal; @@ -139,6 +144,12 @@ public final class DeviceStateManagerService extends SystemService { @NonNull private Optional<OverrideRequest> mActiveOverride = Optional.empty(); + // The current active base state override request. When set the device state specified here will + // replace the value in mBaseState. + @GuardedBy("mLock") + @NonNull + private Optional<OverrideRequest> mActiveBaseStateOverride = Optional.empty(); + // List of processes registered to receive notifications about changes to device state and // request status indexed by process id. @GuardedBy("mLock") @@ -177,7 +188,8 @@ public final class DeviceStateManagerService extends SystemService { mOverrideRequestController = new OverrideRequestController( this::onOverrideRequestStatusChangedLocked); mDeviceStatePolicy = policy; - mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener()); + mDeviceStateProviderListener = new DeviceStateProviderListener(); + mDeviceStatePolicy.getDeviceStateProvider().setListener(mDeviceStateProviderListener); mBinderService = new BinderService(); mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); } @@ -257,6 +269,21 @@ public final class DeviceStateManagerService extends SystemService { } } + /** + * Returns the current override base state, or {@link Optional#empty()} if no override state is + * requested. If an override base state is present, the returned state will be the same as + * the base state returned from {@link #getBaseState()}. + */ + @NonNull + Optional<DeviceState> getOverrideBaseState() { + synchronized (mLock) { + if (mActiveBaseStateOverride.isPresent()) { + return getStateLocked(mActiveBaseStateOverride.get().getRequestedState()); + } + return Optional.empty(); + } + } + /** Returns the list of currently supported device states. */ DeviceState[] getSupportedStates() { synchronized (mLock) { @@ -366,6 +393,7 @@ public final class DeviceStateManagerService extends SystemService { } final DeviceState baseState = baseStateOptional.get(); + if (mBaseState.isPresent() && mBaseState.get().equals(baseState)) { // Base state hasn't changed. Nothing to do. return; @@ -375,7 +403,7 @@ public final class DeviceStateManagerService extends SystemService { if (baseState.hasFlag(FLAG_CANCEL_OVERRIDE_REQUESTS)) { mOverrideRequestController.cancelOverrideRequest(); } - mOverrideRequestController.handleBaseStateChanged(); + mOverrideRequestController.handleBaseStateChanged(identifier); updatePendingStateLocked(); if (!mPendingState.isPresent()) { @@ -528,16 +556,41 @@ public final class DeviceStateManagerService extends SystemService { } } + @GuardedBy("mLock") private void onOverrideRequestStatusChangedLocked(@NonNull OverrideRequest request, @OverrideRequestController.RequestStatus int status) { - if (status == STATUS_ACTIVE) { - mActiveOverride = Optional.of(request); - } else if (status == STATUS_CANCELED) { - if (mActiveOverride.isPresent() && mActiveOverride.get() == request) { - mActiveOverride = Optional.empty(); + if (request.getRequestType() == OVERRIDE_REQUEST_TYPE_BASE_STATE) { + switch (status) { + case STATUS_ACTIVE: + enableBaseStateRequestLocked(request); + return; + case STATUS_CANCELED: + if (mActiveBaseStateOverride.isPresent() + && mActiveBaseStateOverride.get() == request) { + mActiveBaseStateOverride = Optional.empty(); + } + break; + case STATUS_UNKNOWN: // same as default + default: + throw new IllegalArgumentException("Unknown request status: " + status); + } + } else if (request.getRequestType() == OVERRIDE_REQUEST_TYPE_EMULATED_STATE) { + switch (status) { + case STATUS_ACTIVE: + mActiveOverride = Optional.of(request); + break; + case STATUS_CANCELED: + if (mActiveOverride.isPresent() && mActiveOverride.get() == request) { + mActiveOverride = Optional.empty(); + } + break; + case STATUS_UNKNOWN: // same as default + default: + throw new IllegalArgumentException("Unknown request status: " + status); } } else { - throw new IllegalArgumentException("Unknown request status: " + status); + throw new IllegalArgumentException( + "Unknown OverrideRest type: " + request.getRequestType()); } boolean updatedPendingState = updatePendingStateLocked(); @@ -564,6 +617,18 @@ public final class DeviceStateManagerService extends SystemService { mHandler.post(this::notifyPolicyIfNeeded); } + /** + * Sets the new base state of the device and notifies the process that made the base state + * override request that the request is now active. + */ + @GuardedBy("mLock") + private void enableBaseStateRequestLocked(OverrideRequest request) { + setBaseState(request.getRequestedState()); + mActiveBaseStateOverride = Optional.of(request); + ProcessRecord processRecord = mProcessRecords.get(request.getPid()); + processRecord.notifyRequestActiveAsync(request.getToken()); + } + private void registerProcess(int pid, IDeviceStateManagerCallback callback) { synchronized (mLock) { if (mProcessRecords.contains(pid)) { @@ -606,7 +671,8 @@ public final class DeviceStateManagerService extends SystemService { + " has no registered callback."); } - if (mOverrideRequestController.hasRequest(token)) { + if (mOverrideRequestController.hasRequest(token, + OVERRIDE_REQUEST_TYPE_EMULATED_STATE)) { throw new IllegalStateException("Request has already been made for the supplied" + " token: " + token); } @@ -617,7 +683,8 @@ public final class DeviceStateManagerService extends SystemService { + " is not supported."); } - OverrideRequest request = new OverrideRequest(token, callingPid, state, flags); + OverrideRequest request = new OverrideRequest(token, callingPid, state, flags, + OVERRIDE_REQUEST_TYPE_EMULATED_STATE); mOverrideRequestController.addRequest(request); } } @@ -633,6 +700,44 @@ public final class DeviceStateManagerService extends SystemService { } } + private void requestBaseStateOverrideInternal(int state, int flags, int callingPid, + @NonNull IBinder token) { + synchronized (mLock) { + final Optional<DeviceState> deviceState = getStateLocked(state); + if (!deviceState.isPresent()) { + throw new IllegalArgumentException("Requested state: " + state + + " is not supported."); + } + + final ProcessRecord processRecord = mProcessRecords.get(callingPid); + if (processRecord == null) { + throw new IllegalStateException("Process " + callingPid + + " has no registered callback."); + } + + if (mOverrideRequestController.hasRequest(token, + OVERRIDE_REQUEST_TYPE_BASE_STATE)) { + throw new IllegalStateException("Request has already been made for the supplied" + + " token: " + token); + } + + OverrideRequest request = new OverrideRequest(token, callingPid, state, flags, + OVERRIDE_REQUEST_TYPE_BASE_STATE); + mOverrideRequestController.addBaseStateRequest(request); + } + } + + private void cancelBaseStateOverrideInternal(int callingPid) { + synchronized (mLock) { + final ProcessRecord processRecord = mProcessRecords.get(callingPid); + if (processRecord == null) { + throw new IllegalStateException("Process " + callingPid + + " has no registered callback."); + } + setBaseState(mDeviceStateProviderListener.mCurrentBaseState); + } + } + private void dumpInternal(PrintWriter pw) { pw.println("DEVICE STATE MANAGER (dumpsys device_state)"); @@ -729,6 +834,8 @@ public final class DeviceStateManagerService extends SystemService { } private final class DeviceStateProviderListener implements DeviceStateProvider.Listener { + @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int mCurrentBaseState; + @Override public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) { if (newDeviceStates.length == 0) { @@ -743,7 +850,7 @@ public final class DeviceStateManagerService extends SystemService { if (identifier < MINIMUM_DEVICE_STATE || identifier > MAXIMUM_DEVICE_STATE) { throw new IllegalArgumentException("Invalid identifier: " + identifier); } - + mCurrentBaseState = identifier; setBaseState(identifier); } } @@ -895,6 +1002,38 @@ public final class DeviceStateManagerService extends SystemService { } @Override // Binder call + public void requestBaseStateOverride(IBinder token, int state, int flags) { + final int callingPid = Binder.getCallingPid(); + getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, + "Permission required to control base state of device."); + + if (token == null) { + throw new IllegalArgumentException("Request token must not be null."); + } + + final long callingIdentity = Binder.clearCallingIdentity(); + try { + requestBaseStateOverrideInternal(state, flags, callingPid, token); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + + @Override // Binder call + public void cancelBaseStateOverride() { + final int callingPid = Binder.getCallingPid(); + getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, + "Permission required to control base state of device."); + + final long callingIdentity = Binder.clearCallingIdentity(); + try { + cancelBaseStateOverrideInternal(callingPid); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + + @Override // Binder call public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver result) { new DeviceStateManagerShellCommand(DeviceStateManagerService.this) diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java index 659ee75de453..8c6068d89296 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java @@ -16,11 +16,8 @@ package com.android.server.devicestate; -import static android.Manifest.permission.CONTROL_DEVICE_STATE; - import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.Context; import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.DeviceStateRequest; import android.os.Binder; @@ -39,6 +36,8 @@ import java.util.stream.Collectors; public class DeviceStateManagerShellCommand extends ShellCommand { @Nullable private static DeviceStateRequest sLastRequest; + @Nullable + private static DeviceStateRequest sLastBaseStateRequest; private final DeviceStateManagerService mService; private final DeviceStateManager mClient; @@ -58,6 +57,8 @@ public class DeviceStateManagerShellCommand extends ShellCommand { switch (cmd) { case "state": return runState(pw); + case "base-state": + return runBaseState(pw); case "print-state": return runPrintState(pw); case "print-states": @@ -89,10 +90,6 @@ public class DeviceStateManagerShellCommand extends ShellCommand { return 0; } - final Context context = mService.getContext(); - context.enforceCallingOrSelfPermission( - CONTROL_DEVICE_STATE, - "Permission required to request device state."); final long callingIdentity = Binder.clearCallingIdentity(); try { if ("reset".equals(nextArg)) { @@ -127,6 +124,47 @@ public class DeviceStateManagerShellCommand extends ShellCommand { return 0; } + private int runBaseState(PrintWriter pw) { + final String nextArg = getNextArg(); + if (nextArg == null) { + printAllStates(pw); + return 0; + } + + final long callingIdentity = Binder.clearCallingIdentity(); + try { + if ("reset".equals(nextArg)) { + if (sLastBaseStateRequest != null) { + mClient.cancelBaseStateOverride(); + sLastBaseStateRequest = null; + } + } else { + int requestedState = Integer.parseInt(nextArg); + DeviceStateRequest request = DeviceStateRequest.newBuilder(requestedState).build(); + + mClient.requestBaseStateOverride(request, null, null); + + sLastBaseStateRequest = request; + } + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: requested state should be an integer"); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println("Error: " + e.getMessage()); + getErrPrintWriter().println("-------------------"); + getErrPrintWriter().println("Run:"); + getErrPrintWriter().println(""); + getErrPrintWriter().println(" print-states"); + getErrPrintWriter().println(""); + getErrPrintWriter().println("to get the list of currently supported device states"); + return -1; + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + + return 0; + } + private int runPrintState(PrintWriter pw) { Optional<DeviceState> deviceState = mService.getCommittedState(); if (deviceState.isPresent()) { diff --git a/services/core/java/com/android/server/devicestate/OverrideRequest.java b/services/core/java/com/android/server/devicestate/OverrideRequest.java index 35a4c844c710..325e384cb8ad 100644 --- a/services/core/java/com/android/server/devicestate/OverrideRequest.java +++ b/services/core/java/com/android/server/devicestate/OverrideRequest.java @@ -16,9 +16,13 @@ package com.android.server.devicestate; +import android.annotation.IntDef; import android.hardware.devicestate.DeviceStateRequest; import android.os.IBinder; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * A request to override the state managed by {@link DeviceStateManagerService}. * @@ -30,13 +34,47 @@ final class OverrideRequest { private final int mRequestedState; @DeviceStateRequest.RequestFlags private final int mFlags; + @OverrideRequestType + private final int mRequestType; + + /** + * Denotes that the request is meant to override the emulated state of the device. This will + * not change the base (physical) state of the device. + * + * This request type should be used if you are looking to emulate a device state for a feature + * but want the system to be aware of the physical state of the device. + */ + public static final int OVERRIDE_REQUEST_TYPE_EMULATED_STATE = 0; + + /** + * Denotes that the request is meant to override the base (physical) state of the device. + * Overriding the base state may not change the emulated state of the device if there is also an + * override request active for that property. + * + * This request type should only be used for testing, where you want to simulate the physical + * state of the device changing. + */ + public static final int OVERRIDE_REQUEST_TYPE_BASE_STATE = 1; + + /** + * Flags for signifying the type of {@link OverrideRequest}. + * + * @hide + */ + @IntDef(flag = true, prefix = { "REQUEST_TYPE_" }, value = { + OVERRIDE_REQUEST_TYPE_BASE_STATE, + OVERRIDE_REQUEST_TYPE_EMULATED_STATE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface OverrideRequestType {} OverrideRequest(IBinder token, int pid, int requestedState, - @DeviceStateRequest.RequestFlags int flags) { + @DeviceStateRequest.RequestFlags int flags, @OverrideRequestType int requestType) { mToken = token; mPid = pid; mRequestedState = requestedState; mFlags = flags; + mRequestType = requestType; } IBinder getToken() { @@ -55,4 +93,9 @@ final class OverrideRequest { int getFlags() { return mFlags; } + + @OverrideRequestType + int getRequestType() { + return mRequestType; + } } diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java index 01f5a09323cf..e39c7324d7bd 100644 --- a/services/core/java/com/android/server/devicestate/OverrideRequestController.java +++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java @@ -75,6 +75,8 @@ final class OverrideRequestController { // Handle to the current override request, null if none. private OverrideRequest mRequest; + // Handle to the current base state override request, null if none. + private OverrideRequest mBaseStateRequest; private boolean mStickyRequestsAllowed; // The current request has outlived their process. @@ -111,13 +113,23 @@ final class OverrideRequestController { } } + void addBaseStateRequest(@NonNull OverrideRequest request) { + OverrideRequest previousRequest = mBaseStateRequest; + mBaseStateRequest = request; + mListener.onStatusChanged(request, STATUS_ACTIVE); + + if (previousRequest != null) { + cancelRequestLocked(previousRequest); + } + } + /** * Cancels the request with the specified {@code token} and notifies the listener of all changes * to request status as a result of this operation. */ void cancelRequest(@NonNull OverrideRequest request) { // Either don't have a current request or attempting to cancel an already cancelled request - if (!hasRequest(request.getToken())) { + if (!hasRequest(request.getToken(), request.getRequestType())) { return; } cancelCurrentRequestLocked(); @@ -144,11 +156,24 @@ final class OverrideRequestController { } /** + * Cancels the current base state override request, this could be due to the physical + * configuration of the device changing. + */ + void cancelBaseStateOverrideRequest() { + cancelCurrentBaseStateRequestLocked(); + } + + /** * Returns {@code true} if this controller is current managing a request with the specified * {@code token}, {@code false} otherwise. */ - boolean hasRequest(@NonNull IBinder token) { - return mRequest != null && token == mRequest.getToken(); + boolean hasRequest(@NonNull IBinder token, + @OverrideRequest.OverrideRequestType int requestType) { + if (requestType == OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE) { + return mBaseStateRequest != null && token == mBaseStateRequest.getToken(); + } else { + return mRequest != null && token == mRequest.getToken(); + } } /** @@ -157,11 +182,11 @@ final class OverrideRequestController { * operation. */ void handleProcessDied(int pid) { - if (mRequest == null) { - return; + if (mBaseStateRequest != null && mBaseStateRequest.getPid() == pid) { + cancelCurrentBaseStateRequestLocked(); } - if (mRequest.getPid() == pid) { + if (mRequest != null && mRequest.getPid() == pid) { if (mStickyRequestsAllowed) { // Do not cancel the requests now because sticky requests are allowed. These // requests will be cancelled on a call to cancelStickyRequests(). @@ -176,7 +201,10 @@ final class OverrideRequestController { * Notifies the controller that the base state has changed. The controller will notify the * listener of all changes to request status as a result of this change. */ - void handleBaseStateChanged() { + void handleBaseStateChanged(int state) { + if (mBaseStateRequest != null && state != mBaseStateRequest.getRequestedState()) { + cancelBaseStateOverrideRequest(); + } if (mRequest == null) { return; } @@ -192,11 +220,12 @@ final class OverrideRequestController { * notify the listener of all changes to request status as a result of this change. */ void handleNewSupportedStates(int[] newSupportedStates) { - if (mRequest == null) { - return; + if (mBaseStateRequest != null && !contains(newSupportedStates, + mBaseStateRequest.getRequestedState())) { + cancelCurrentBaseStateRequestLocked(); } - if (!contains(newSupportedStates, mRequest.getRequestedState())) { + if (mRequest != null && !contains(newSupportedStates, mRequest.getRequestedState())) { cancelCurrentRequestLocked(); } } @@ -228,10 +257,23 @@ final class OverrideRequestController { return; } mStickyRequest = false; - mListener.onStatusChanged(mRequest, STATUS_CANCELED); + cancelRequestLocked(mRequest); mRequest = null; } + /** + * Handles cancelling {@code mBaseStateRequest}. + * Notifies the listener of the canceled status as well. + */ + private void cancelCurrentBaseStateRequestLocked() { + if (mBaseStateRequest == null) { + Slog.w(TAG, "Attempted to cancel a null OverrideRequest"); + return; + } + cancelRequestLocked(mBaseStateRequest); + mBaseStateRequest = null; + } + private static boolean contains(int[] array, int value) { for (int i = 0; i < array.length; i++) { if (array[i] == value) { diff --git a/services/core/java/com/android/server/display/WifiDisplayController.java b/services/core/java/com/android/server/display/WifiDisplayController.java index a7e1a2876f81..cd36dd02a8c6 100644 --- a/services/core/java/com/android/server/display/WifiDisplayController.java +++ b/services/core/java/com/android/server/display/WifiDisplayController.java @@ -885,7 +885,7 @@ final class WifiDisplayController implements DumpUtils.Dump { } }); } - } else { + } else if (!networkInfo.isConnectedOrConnecting()) { mConnectedDeviceGroupInfo = null; // Disconnect if we lost the network while connecting or connected to a display. diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java index 1bcc21e66302..43732d473cf4 100644 --- a/services/core/java/com/android/server/logcat/LogcatManagerService.java +++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java @@ -16,6 +16,8 @@ package com.android.server.logcat; +import static android.os.Process.getParentPid; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -38,6 +40,8 @@ import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.ILogAccessDialogCallback; +import com.android.internal.app.LogAccessDialogActivity; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -45,6 +49,7 @@ import com.android.server.SystemService; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; @@ -98,7 +103,7 @@ public final class LogcatManagerService extends SystemService { private final Injector mInjector; private final Supplier<Long> mClock; private final BinderService mBinderService; - private final LogcatManagerServiceInternal mLocalService; + private final LogAccessDialogCallback mDialogCallback; private final Handler mHandler; private ActivityManagerInternal mActivityManagerInternal; private ILogd mLogdService; @@ -203,7 +208,8 @@ public final class LogcatManagerService extends SystemService { } } - final class LogcatManagerServiceInternal { + final class LogAccessDialogCallback extends ILogAccessDialogCallback.Stub { + @Override public void approveAccessForClient(int uid, @NonNull String packageName) { final LogAccessClient client = new LogAccessClient(uid, packageName); if (DEBUG) { @@ -213,6 +219,7 @@ public final class LogcatManagerService extends SystemService { mHandler.sendMessageAtTime(msg, mClock.get()); } + @Override public void declineAccessForClient(int uid, @NonNull String packageName) { final LogAccessClient client = new LogAccessClient(uid, packageName); if (DEBUG) { @@ -299,7 +306,7 @@ public final class LogcatManagerService extends SystemService { mInjector = injector; mClock = injector.createClock(); mBinderService = new BinderService(); - mLocalService = new LogcatManagerServiceInternal(); + mDialogCallback = new LogAccessDialogCallback(); mHandler = new LogAccessRequestHandler(injector.getLooper(), this); } @@ -308,15 +315,14 @@ public final class LogcatManagerService extends SystemService { try { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); publishBinderService("logcat", mBinderService); - publishLocalService(LogcatManagerServiceInternal.class, mLocalService); } catch (Throwable t) { Slog.e(TAG, "Could not start the LogcatManagerService.", t); } } @VisibleForTesting - LogcatManagerServiceInternal getLocalService() { - return mLocalService; + LogAccessDialogCallback getDialogCallback() { + return mDialogCallback; } @VisibleForTesting @@ -340,13 +346,6 @@ public final class LogcatManagerService extends SystemService { * access */ private String getPackageName(LogAccessRequest request) { - if (mActivityManagerInternal != null) { - String packageName = mActivityManagerInternal.getPackageNameByPid(request.mPid); - if (packageName != null) { - return packageName; - } - } - PackageManager pm = mContext.getPackageManager(); if (pm == null) { // Decline the logd access if PackageManager is null @@ -355,15 +354,28 @@ public final class LogcatManagerService extends SystemService { } String[] packageNames = pm.getPackagesForUid(request.mUid); - if (ArrayUtils.isEmpty(packageNames)) { // Decline the logd access if the app name is unknown Slog.e(TAG, "Unknown calling package name, declining the logd access"); return null; } - String firstPackageName = packageNames[0]; + if (mActivityManagerInternal != null) { + int pid = request.mPid; + String packageName = mActivityManagerInternal.getPackageNameByPid(pid); + while ((packageName == null || !ArrayUtils.contains(packageNames, packageName)) + && pid != -1) { + pid = getParentPid(pid); + packageName = mActivityManagerInternal.getPackageNameByPid(pid); + } + + if (packageName != null && ArrayUtils.contains(packageNames, packageName)) { + return packageName; + } + } + Arrays.sort(packageNames); + String firstPackageName = packageNames[0]; if (firstPackageName == null || firstPackageName.isEmpty()) { // Decline the logd access if the package name from uid is unknown Slog.e(TAG, "Unknown calling package name, declining the logd access"); @@ -430,6 +442,7 @@ public final class LogcatManagerService extends SystemService { mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_PENDING_TIMEOUT, client), mClock.get() + PENDING_CONFIRMATION_TIMEOUT_MILLIS); final Intent mIntent = createIntent(client); + mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM); } @@ -530,6 +543,7 @@ public final class LogcatManagerService extends SystemService { intent.putExtra(Intent.EXTRA_PACKAGE_NAME, client.mPackageName); intent.putExtra(Intent.EXTRA_UID, client.mUid); + intent.putExtra(LogAccessDialogActivity.EXTRA_CALLBACK, mDialogCallback.asBinder()); return intent; } diff --git a/services/core/java/com/android/server/pm/AppsFilterBase.java b/services/core/java/com/android/server/pm/AppsFilterBase.java index 5de7674a7422..01252c48081e 100644 --- a/services/core/java/com/android/server/pm/AppsFilterBase.java +++ b/services/core/java/com/android/server/pm/AppsFilterBase.java @@ -205,12 +205,12 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot { return mQueriesViaComponent.contains(callingAppId, targetAppId); } - protected boolean isImplicitlyQueryable(int callingAppId, int targetAppId) { - return mImplicitlyQueryable.contains(callingAppId, targetAppId); + protected boolean isImplicitlyQueryable(int callingUid, int targetUid) { + return mImplicitlyQueryable.contains(callingUid, targetUid); } - protected boolean isRetainedImplicitlyQueryable(int callingAppId, int targetAppId) { - return mRetainedImplicitlyQueryable.contains(callingAppId, targetAppId); + protected boolean isRetainedImplicitlyQueryable(int callingUid, int targetUid) { + return mRetainedImplicitlyQueryable.contains(callingUid, targetUid); } protected boolean isQueryableViaUsesLibrary(int callingAppId, int targetAppId) { @@ -322,9 +322,11 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot { || callingAppId == targetPkgSetting.getAppId()) { return false; } else if (Process.isSdkSandboxUid(callingAppId)) { + final int targetAppId = targetPkgSetting.getAppId(); + final int targetUid = UserHandle.getUid(userId, targetAppId); // we only allow sdk sandbox processes access to forcequeryable packages return !isForceQueryable(targetPkgSetting.getAppId()) - && !isImplicitlyQueryable(callingAppId, targetPkgSetting.getAppId()); + && !isImplicitlyQueryable(callingUid, targetUid); } if (mCacheReady) { // use cache if (!shouldFilterApplicationUsingCache(callingUid, diff --git a/services/core/java/com/android/server/pm/AppsFilterLocked.java b/services/core/java/com/android/server/pm/AppsFilterLocked.java index 30eb09e61d3f..cc71b0540dde 100644 --- a/services/core/java/com/android/server/pm/AppsFilterLocked.java +++ b/services/core/java/com/android/server/pm/AppsFilterLocked.java @@ -65,16 +65,16 @@ abstract class AppsFilterLocked extends AppsFilterBase { } @Override - protected boolean isImplicitlyQueryable(int callingAppId, int targetAppId) { + protected boolean isImplicitlyQueryable(int callingUid, int targetUid) { synchronized (mImplicitlyQueryableLock) { - return super.isImplicitlyQueryable(callingAppId, targetAppId); + return super.isImplicitlyQueryable(callingUid, targetUid); } } @Override - protected boolean isRetainedImplicitlyQueryable(int callingAppId, int targetAppId) { + protected boolean isRetainedImplicitlyQueryable(int callingUid, int targetUid) { synchronized (mImplicitlyQueryableLock) { - return super.isRetainedImplicitlyQueryable(callingAppId, targetAppId); + return super.isRetainedImplicitlyQueryable(callingUid, targetUid); } } diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 4a640ce6274c..46b7460dff1b 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -1192,24 +1192,27 @@ public class ComputerEngine implements Computer { } allHomeCandidates.addAll(resolveInfos); - String packageName = mDefaultAppProvider.getDefaultHome(userId); + String packageName = null; + // Workaround for b/237330774 in T: return the preferred activity first to honor + // persistent preferred activity. + // Role changes are not and cannot be atomic because its implementation lives inside + // a system app, so when the home role changes, there is a window when the previous + // role holder is removed and the new role holder is granted the preferred activity, + // but hasn't become the role holder yet. However, this case may be easily hit + // because the preferred activity change triggers a broadcast and receivers may try + // to get the default home activity there. So we need to fix it for this time + // window, and an easy workaround is to fallback to the current preferred activity. + final int appId = UserHandle.getAppId(Binder.getCallingUid()); + final boolean filtered = appId >= Process.FIRST_APPLICATION_UID; + PackageManagerService.FindPreferredActivityBodyResult result = + findPreferredActivityInternal(intent, null, 0, resolveInfos, true, false, + false, userId, filtered); + ResolveInfo preferredResolveInfo = result.mPreferredResolveInfo; + if (preferredResolveInfo != null && preferredResolveInfo.activityInfo != null) { + packageName = preferredResolveInfo.activityInfo.packageName; + } if (packageName == null) { - // Role changes are not and cannot be atomic because its implementation lives inside - // a system app, so when the home role changes, there is a window when the previous - // role holder is removed and the new role holder is granted the preferred activity, - // but hasn't become the role holder yet. However, this case may be easily hit - // because the preferred activity change triggers a broadcast and receivers may try - // to get the default home activity there. So we need to fix it for this time - // window, and an easy workaround is to fallback to the current preferred activity. - final int appId = UserHandle.getAppId(Binder.getCallingUid()); - final boolean filtered = appId >= Process.FIRST_APPLICATION_UID; - PackageManagerService.FindPreferredActivityBodyResult result = - findPreferredActivityInternal(intent, null, 0, resolveInfos, true, false, - false, userId, filtered); - ResolveInfo preferredResolveInfo = result.mPreferredResolveInfo; - if (preferredResolveInfo != null && preferredResolveInfo.activityInfo != null) { - packageName = preferredResolveInfo.activityInfo.packageName; - } + packageName = mDefaultAppProvider.getDefaultHome(userId); } if (packageName == null) { return null; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index e9d54268560a..b7b332621e7f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -6546,7 +6546,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService if (dependentState == null) { continue; } - if (!Objects.equals(dependentState.getUserStateOrDefault(userId) + if (canSetOverlayPaths(dependentState.getUserStateOrDefault(userId) .getSharedLibraryOverlayPaths() .get(libName), newOverlayPaths)) { String dependentPackageName = dependent.getPackageName(); @@ -6562,7 +6562,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService } } - outUpdatedPackageNames.add(targetPackageName); + if (canSetOverlayPaths(packageState.getUserStateOrDefault(userId).getOverlayPaths(), + newOverlayPaths)) { + outUpdatedPackageNames.add(targetPackageName); + } commitPackageStateMutation(null, mutator -> { mutator.forPackage(targetPackageName) @@ -6593,6 +6596,17 @@ public class PackageManagerService implements PackageSender, TestUtilityService return true; } + private boolean canSetOverlayPaths(OverlayPaths origPaths, OverlayPaths newPaths) { + if (Objects.equals(origPaths, newPaths)) { + return false; + } + if ((origPaths == null && newPaths.isEmpty()) + || (newPaths == null && origPaths.isEmpty())) { + return false; + } + return true; + } + private void maybeUpdateSystemOverlays(String targetPackageName, OverlayPaths newOverlayPaths) { if (!mResolverReplaced) { if (targetPackageName.equals("android")) { diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java index 7ad4d226d801..eebde3613fac 100644 --- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java +++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java @@ -110,12 +110,12 @@ public final class SuspendPackageHelper { final SuspendParams newSuspendParams = new SuspendParams(dialogInfo, appExtras, launcherExtras); - final List<String> changedPackagesList = new ArrayList<>(packageNames.length); - final IntArray changedUids = new IntArray(packageNames.length); - final IntArray modifiedUids = new IntArray(packageNames.length); final List<String> unmodifiablePackages = new ArrayList<>(packageNames.length); - ArraySet<String> modifiedPackages = new ArraySet<>(); + final List<String> notifyPackagesList = new ArrayList<>(packageNames.length); + final IntArray notifyUids = new IntArray(packageNames.length); + final ArraySet<String> changedPackagesList = new ArraySet<>(packageNames.length); + final IntArray changedUids = new IntArray(packageNames.length); final boolean[] canSuspend = suspended ? canSuspendPackageForUser(snapshot, packageNames, userId, callingUid) : null; @@ -143,21 +143,16 @@ public final class SuspendPackageHelper { final WatchedArrayMap<String, SuspendParams> suspendParamsMap = packageState.getUserStateOrDefault(userId).getSuspendParams(); - if (suspended) { - if (suspendParamsMap != null && suspendParamsMap.containsKey(packageName)) { - final SuspendParams suspendParams = suspendParamsMap.get(packageName); - // Skip if there's no changes - if (suspendParams != null - && Objects.equals(suspendParams.getDialogInfo(), dialogInfo) - && Objects.equals(suspendParams.getAppExtras(), appExtras) - && Objects.equals(suspendParams.getLauncherExtras(), - launcherExtras)) { - // Carried over API behavior, must notify change even if no change - changedPackagesList.add(packageName); - changedUids.add(UserHandle.getUid(userId, packageState.getAppId())); - continue; - } - } + + SuspendParams oldSuspendParams = suspendParamsMap == null + ? null : suspendParamsMap.get(packageName); + boolean changed = !Objects.equals(oldSuspendParams, newSuspendParams); + + if (suspended && !changed) { + // Carried over API behavior, must notify change even if no change + notifyPackagesList.add(packageName); + notifyUids.add(UserHandle.getUid(userId, packageState.getAppId())); + continue; } // If only the callingPackage is suspending this package, @@ -166,18 +161,21 @@ public final class SuspendPackageHelper { && CollectionUtils.size(suspendParamsMap) == 1 && suspendParamsMap.containsKey(callingPackage); if (suspended || packageUnsuspended) { + // Always notify of a suspend call + notify when fully unsuspended + notifyPackagesList.add(packageName); + notifyUids.add(UserHandle.getUid(userId, packageState.getAppId())); + } + + if (changed) { changedPackagesList.add(packageName); changedUids.add(UserHandle.getUid(userId, packageState.getAppId())); } - - modifiedPackages.add(packageName); - modifiedUids.add(UserHandle.getUid(userId, packageState.getAppId())); } mPm.commitPackageStateMutation(null, mutator -> { - final int size = modifiedPackages.size(); + final int size = changedPackagesList.size(); for (int index = 0; index < size; index++) { - final String packageName = modifiedPackages.valueAt(index); + final String packageName = changedPackagesList.valueAt(index); final PackageUserStateWrite userState = mutator.forPackage(packageName) .userState(userId); if (suspended) { @@ -190,19 +188,19 @@ public final class SuspendPackageHelper { final Computer newSnapshot = mPm.snapshotComputer(); - if (!changedPackagesList.isEmpty()) { - final String[] changedPackages = changedPackagesList.toArray(new String[0]); + if (!notifyPackagesList.isEmpty()) { + final String[] notifyPackages = notifyPackagesList.toArray(new String[0]); sendPackagesSuspendedForUser(newSnapshot, suspended ? Intent.ACTION_PACKAGES_SUSPENDED : Intent.ACTION_PACKAGES_UNSUSPENDED, - changedPackages, changedUids.toArray(), userId); - sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId); + notifyPackages, notifyUids.toArray(), userId); + sendMyPackageSuspendedOrUnsuspended(notifyPackages, suspended, userId); mPm.scheduleWritePackageRestrictions(userId); } // Send the suspension changed broadcast to ensure suspension state is not stale. - if (!modifiedPackages.isEmpty()) { + if (!changedPackagesList.isEmpty()) { sendPackagesSuspendedForUser(newSnapshot, Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, - modifiedPackages.toArray(new String[0]), modifiedUids.toArray(), userId); + changedPackagesList.toArray(new String[0]), changedUids.toArray(), userId); } return unmodifiablePackages.toArray(new String[0]); } diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java index dc4bdaaa8158..ce1157e1d80c 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java @@ -18,6 +18,7 @@ package com.android.server.soundtrigger_middleware; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; import android.media.permission.Identity; import android.media.permission.IdentityContext; import android.media.soundtrigger.ModelParameterRange; @@ -33,6 +34,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import com.android.internal.util.LatencyTracker; + import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; @@ -65,9 +68,12 @@ import java.util.Objects; public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInternal, Dumpable { private static final String TAG = "SoundTriggerMiddlewareLogging"; private final @NonNull ISoundTriggerMiddlewareInternal mDelegate; + private final @NonNull Context mContext; - public SoundTriggerMiddlewareLogging(@NonNull ISoundTriggerMiddlewareInternal delegate) { + public SoundTriggerMiddlewareLogging(@NonNull Context context, + @NonNull ISoundTriggerMiddlewareInternal delegate) { mDelegate = delegate; + mContext = context; } @Override @@ -298,6 +304,7 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt int captureSession) throws RemoteException { try { + startKeyphraseEventLatencyTracking(event); mCallbackDelegate.onPhraseRecognition(modelHandle, event, captureSession); logVoidReturn("onPhraseRecognition", modelHandle, event); } catch (Exception e) { @@ -347,6 +354,26 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt logVoidReturnWithObject(this, mOriginatorIdentity, methodName, args); } + /** + * Starts the latency tracking log for keyphrase hotword invocation. + * The measurement covers from when the SoundTrigger HAL emits an event to when the + * {@link android.service.voice.VoiceInteractionSession} system UI view is shown. + */ + private void startKeyphraseEventLatencyTracking(PhraseRecognitionEvent event) { + String latencyTrackerTag = null; + if (event.phraseExtras.length > 0) { + latencyTrackerTag = "KeyphraseId=" + event.phraseExtras[0].id; + } + LatencyTracker latencyTracker = LatencyTracker.getInstance(mContext); + // To avoid adding cancel to all of the different failure modes between here and + // showing the system UI, we defensively cancel once. + // Either we hit the LatencyTracker timeout of 15 seconds or we defensively cancel + // here if any error occurs. + latencyTracker.onActionCancel(LatencyTracker.ACTION_SHOW_VOICE_INTERACTION); + latencyTracker.onActionStart(LatencyTracker.ACTION_SHOW_VOICE_INTERACTION, + latencyTrackerTag); + } + @Override public IBinder asBinder() { return mCallbackDelegate.asBinder(); diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java index 1995e5497e55..807ed14e85ce 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java @@ -20,14 +20,14 @@ import static android.Manifest.permission.SOUNDTRIGGER_DELEGATE_IDENTITY; import android.annotation.NonNull; import android.content.Context; -import android.media.soundtrigger.ModelParameterRange; -import android.media.soundtrigger.PhraseSoundModel; -import android.media.soundtrigger.RecognitionConfig; -import android.media.soundtrigger.SoundModel; import android.media.permission.ClearCallingIdentityContext; import android.media.permission.Identity; import android.media.permission.PermissionUtil; import android.media.permission.SafeCloseable; +import android.media.soundtrigger.ModelParameterRange; +import android.media.soundtrigger.PhraseSoundModel; +import android.media.soundtrigger.RecognitionConfig; +import android.media.soundtrigger.SoundModel; import android.media.soundtrigger_middleware.ISoundTriggerCallback; import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService; import android.media.soundtrigger_middleware.ISoundTriggerModule; @@ -226,12 +226,13 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic HalFactory[] factories = new HalFactory[]{new DefaultHalFactory()}; publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE, - new SoundTriggerMiddlewareService(new SoundTriggerMiddlewareLogging( - new SoundTriggerMiddlewarePermission( - new SoundTriggerMiddlewareValidation( - new SoundTriggerMiddlewareImpl(factories, - new AudioSessionProviderImpl())), - getContext())), getContext())); + new SoundTriggerMiddlewareService( + new SoundTriggerMiddlewareLogging(getContext(), + new SoundTriggerMiddlewarePermission( + new SoundTriggerMiddlewareValidation( + new SoundTriggerMiddlewareImpl(factories, + new AudioSessionProviderImpl())), + getContext())), getContext())); } } } diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 7170773edca4..f888ff60b12a 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -687,7 +687,7 @@ public class TrustManagerService extends SystemService { */ public void lockUser(int userId) { mLockPatternUtils.requireStrongAuth( - StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId); + StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, userId); try { WindowManagerGlobal.getWindowManagerService().lockNow(null); } catch (RemoteException e) { @@ -2084,7 +2084,7 @@ public class TrustManagerService extends SystemService { if (mStrongAuthTracker.isTrustAllowedForUser(mUserId)) { if (DEBUG) Slog.d(TAG, "Revoking all trust because of trust timeout"); mLockPatternUtils.requireStrongAuth( - mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, mUserId); + mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, mUserId); } maybeLockScreen(mUserId); } diff --git a/services/core/java/com/android/server/utils/AlarmQueue.java b/services/core/java/com/android/server/utils/AlarmQueue.java index 3f4def6d60ab..c9bfc8d74ecd 100644 --- a/services/core/java/com/android/server/utils/AlarmQueue.java +++ b/services/core/java/com/android/server/utils/AlarmQueue.java @@ -34,6 +34,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import java.util.Comparator; import java.util.PriorityQueue; import java.util.function.Predicate; @@ -58,8 +59,11 @@ public abstract class AlarmQueue<K> implements AlarmManager.OnAlarmListener { * The pair is the key and its associated alarm time (in the elapsed realtime timebase). */ private static class AlarmPriorityQueue<Q> extends PriorityQueue<Pair<Q, Long>> { + private static final Comparator<Pair<?, Long>> sTimeComparator = + (o1, o2) -> Long.compare(o1.second, o2.second); + AlarmPriorityQueue() { - super(1, (o1, o2) -> (int) (o1.second - o2.second)); + super(1, sTimeComparator); } /** diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index e9fbeb614dfa..b8486e7aa2b4 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2764,6 +2764,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final StartingSurfaceController.StartingSurface surface; final StartingData startingData = mStartingData; + final WindowState startingWindow = mStartingWindow; if (mStartingData != null) { surface = mStartingSurface; mStartingData = null; @@ -2782,21 +2783,31 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } - ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Schedule remove starting %s startingWindow=%s" + " startingView=%s Callers=%s", this, mStartingWindow, mStartingSurface, Debug.getCallers(5)); - + final boolean removeWithAnimate = prepareAnimation && startingData.needRevealAnimation(); final Runnable removeSurface = () -> { ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Removing startingView=%s", surface); try { - surface.remove(prepareAnimation && startingData.needRevealAnimation()); + surface.remove(removeWithAnimate); } catch (Exception e) { Slog.w(TAG_WM, "Exception when removing starting window", e); } }; - - removeSurface.run(); + if (removeWithAnimate && mTransitionController.inCollectingTransition(startingWindow) + && startingWindow.cancelAndRedraw()) { + // Defer remove starting window after transition start. + // If splash screen window was in collecting, the client side is unable to draw because + // of Session#cancelDraw, which will blocking the remove animation. + startingWindow.mSyncTransaction.addTransactionCommittedListener(Runnable::run, () -> { + synchronized (mAtmService.mGlobalLock) { + removeSurface.run(); + } + }); + } else { + removeSurface.run(); + } } /** diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 75e24a89048b..0398cc84f9db 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -68,6 +68,7 @@ import static android.view.WindowManager.TRANSIT_PIP; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DREAM; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK; @@ -1446,6 +1447,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { boolean canLaunchDreamActivity(String packageName) { if (!mDreaming || packageName == null) { + ProtoLog.e(WM_DEBUG_DREAM, "Cannot launch dream activity due to invalid state. " + + "dreaming: %b packageName: %s", mDreaming, packageName); return false; } final DreamManagerInternal dreamManager = @@ -1461,6 +1464,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (activeDoze != null && packageName.equals(activeDoze.getPackageName())) { return true; } + ProtoLog.e(WM_DEBUG_DREAM, + "Dream packageName does not match active dream. Package %s does not match %s or %s", + packageName, String.valueOf(activeDream), String.valueOf(activeDoze)); return false; } diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java index 1266db5bff98..8c5f05365837 100644 --- a/services/core/java/com/android/server/wm/AsyncRotationController.java +++ b/services/core/java/com/android/server/wm/AsyncRotationController.java @@ -202,10 +202,16 @@ class AsyncRotationController extends FadeAnimationController implements Consume // target windows. But the windows still need to use sync transaction to keep the appearance // in previous rotation, so request a no-op sync to keep the state. for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { + if (TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST + && mTargetWindowTokens.valueAt(i).mAction != Operation.ACTION_SEAMLESS) { + // Expect a screenshot layer will cover the non seamless windows. + continue; + } final WindowToken token = mTargetWindowTokens.keyAt(i); for (int j = token.getChildCount() - 1; j >= 0; j--) { // TODO(b/234585256): The consumer should be handleFinishDrawing(). token.getChildAt(j).applyWithNextDraw(t -> {}); + if (DEBUG) Slog.d(TAG, "Sync draw for " + token.getChildAt(j)); } } mIsSyncDrawRequested = true; diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java index 35a744f70d27..f304b4a009b5 100644 --- a/services/core/java/com/android/server/wm/ContentRecorder.java +++ b/services/core/java/com/android/server/wm/ContentRecorder.java @@ -388,8 +388,7 @@ final class ContentRecorder implements WindowContainerListener { * </p> */ private void handleStartRecordingFailed() { - final boolean shouldExitTaskRecording = mContentRecordingSession != null - && mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK; + final boolean shouldExitTaskRecording = isRecordingContentTask(); clearContentRecordingSession(); if (shouldExitTaskRecording) { // Clean up the cached session first to ensure recording doesn't re-start, since @@ -475,9 +474,10 @@ final class ContentRecorder implements WindowContainerListener { ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Recorded task is removed, so stop recording on display %d", mDisplayContent.getDisplayId()); - Task recordedTask = mRecordedWindowContainer.asTask(); - if (recordedTask == null - || mContentRecordingSession.getContentToRecord() != RECORD_CONTENT_TASK) { + + Task recordedTask = mRecordedWindowContainer != null + ? mRecordedWindowContainer.asTask() : null; + if (recordedTask == null || !isRecordingContentTask()) { return; } recordedTask.unregisterWindowContainerListener(this); @@ -501,4 +501,9 @@ final class ContentRecorder implements WindowContainerListener { @VisibleForTesting interface MediaProjectionManagerWrapper { void stopActiveProjection(); } + + private boolean isRecordingContentTask() { + return mContentRecordingSession != null + && mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK; + } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 5b702eac7059..353ca53a2e50 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -116,7 +116,7 @@ public class RecentsAnimationController implements DeathRecipient { private boolean mWillFinishToHome = false; private final Runnable mFailsafeRunnable = this::onFailsafe; - // The recents component app token that is shown behind the visibile tasks + // The recents component app token that is shown behind the visible tasks private ActivityRecord mTargetActivityRecord; private DisplayContent mDisplayContent; private int mTargetActivityType; @@ -457,6 +457,22 @@ public class RecentsAnimationController implements DeathRecipient { } } + /** + * Return whether the given window should still be considered interesting for the all-drawn + * state. This is only interesting for the target app, which may have child windows that are + * not actually visible and should not be considered interesting and waited upon. + */ + protected boolean isInterestingForAllDrawn(WindowState window) { + if (isTargetApp(window.getActivityRecord())) { + if (window.getWindowType() != TYPE_BASE_APPLICATION + && window.getAttrs().alpha == 0f) { + // If there is a cihld window that is alpha 0, then ignore that window + return false; + } + } + // By default all windows are still interesting for all drawn purposes + return true; + } /** * Whether a task should be filtered from the recents animation. This can be true for tasks diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 488fe676d265..147c9cb18315 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -48,6 +48,7 @@ import static android.view.WindowManager.transitTypeToString; import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS; import static android.window.TransitionInfo.FLAG_FILLS_TASK; import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY; +import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD; import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION; @@ -1894,14 +1895,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // Whether this is in a Task with embedded activity. flags |= FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY; } - final Rect taskBounds = parentTask.getBounds(); - final Rect startBounds = mAbsoluteBounds; - final Rect endBounds = wc.getBounds(); - if (taskBounds.width() == startBounds.width() - && taskBounds.height() == startBounds.height() - && taskBounds.width() == endBounds.width() - && taskBounds.height() == endBounds.height()) { - // Whether the container fills the Task bounds before and after the transition. + if (parentTask.forAllActivities(ActivityRecord::hasStartingWindow)) { + // The starting window should cover all windows inside the leaf Task. + flags |= FLAG_IS_BEHIND_STARTING_WINDOW; + } + if (isWindowFillingTask(wc, parentTask)) { + // Whether the container fills its parent Task bounds. flags |= FLAG_FILLS_TASK; } } @@ -1923,6 +1922,22 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } return flags; } + + /** Whether the container fills its parent Task bounds before and after the transition. */ + private boolean isWindowFillingTask(@NonNull WindowContainer wc, @NonNull Task parentTask) { + final Rect taskBounds = parentTask.getBounds(); + final int taskWidth = taskBounds.width(); + final int taskHeight = taskBounds.height(); + final Rect startBounds = mAbsoluteBounds; + final Rect endBounds = wc.getBounds(); + // Treat it as filling the task if it is not visible. + final boolean isInvisibleOrFillingTaskBeforeTransition = !mVisible + || (taskWidth == startBounds.width() && taskHeight == startBounds.height()); + final boolean isInVisibleOrFillingTaskAfterTransition = !wc.isVisibleRequested() + || (taskWidth == endBounds.width() && taskHeight == endBounds.height()); + return isInvisibleOrFillingTaskBeforeTransition + && isInVisibleOrFillingTaskAfterTransition; + } } /** diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 383fcb9303d7..07ae167f5e66 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -477,7 +477,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } mLocalInsetsSourceProviders.remove(insetsTypes[i]); } - mDisplayContent.getInsetsStateController().updateAboveInsetsState(true); + // Update insets if this window is attached. + if (mDisplayContent != null) { + mDisplayContent.getInsetsStateController().updateAboveInsetsState(true); + } } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c9115e4dd7fc..c96253ccf70a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8514,7 +8514,10 @@ public class WindowManagerService extends IWindowManager.Stub if (mFocusedInputTarget != t && mFocusedInputTarget != null) { mFocusedInputTarget.handleTapOutsideFocusOutsideSelf(); } + // Trigger Activity#onUserLeaveHint() if the order change of task pauses any activities. + mAtmService.mTaskSupervisor.mUserLeaving = true; t.handleTapOutsideFocusInsideSelf(); + mAtmService.mTaskSupervisor.mUserLeaving = false; } @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 9456f0fcced7..2e1477ddf0f1 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -25,6 +25,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_FINISH_ACTIVITY; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER; @@ -1007,6 +1008,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub isInLockTaskMode); break; } + case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: { + final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer()); + if (activity == null || activity.finishing) { + break; + } + if (activity.isVisible()) { + // Prevent the transition from being executed too early if the activity is + // visible. + activity.finishIfPossible("finish-activity-op", false /* oomAdj */); + } else { + activity.destroyIfPossible("finish-activity-op"); + } + break; + } case HIERARCHY_OP_TYPE_LAUNCH_TASK: { mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS, "launchTask HierarchyOp"); @@ -1620,6 +1635,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub organizer); } break; + case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: + // Allow finish activity if it has the activity token. + break; default: // Other types of hierarchy changes are not allowed. String msg = "Permission Denial: " + func + " from pid=" diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index fd18d3de180e..7c9d27704442 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2043,9 +2043,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * it must be drawn before allDrawn can become true. */ boolean isInteresting() { + final RecentsAnimationController recentsAnimationController = + mWmService.getRecentsAnimationController(); return mActivityRecord != null && !mAppDied && (!mActivityRecord.isFreezingScreen() || !mAppFreezing) - && mViewVisibility == View.VISIBLE; + && mViewVisibility == View.VISIBLE + && (recentsAnimationController == null + || recentsAnimationController.isInterestingForAllDrawn(this)); } /** @@ -3468,7 +3472,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP "Setting visibility of " + this + ": " + clientVisible); mClient.dispatchAppVisibility(clientVisible); } catch (RemoteException e) { + // The remote client fails to process the visibility message. That means it is in a + // wrong state. E.g. the binder buffer is running out or the binder threads are dead. + // The window visibility is out-of-sync that may cause blank content or left over, so + // just kill it. And if it is a window of foreground activity, the activity can be + // restarted automatically if needed. Slog.w(TAG, "Exception thrown during dispatchAppVisibility " + this, e); + android.os.Process.killProcess(mSession.mPid); } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 8fd4b5aa6bee..2ee4af56d320 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -338,8 +338,6 @@ public final class SystemServer implements Dumpable { "com.android.server.contentcapture.ContentCaptureManagerService"; private static final String TRANSLATION_MANAGER_SERVICE_CLASS = "com.android.server.translation.TranslationManagerService"; - private static final String SELECTION_TOOLBAR_MANAGER_SERVICE_CLASS = - "com.android.server.selectiontoolbar.SelectionToolbarManagerService"; private static final String MUSIC_RECOGNITION_MANAGER_SERVICE_CLASS = "com.android.server.musicrecognition.MusicRecognitionManagerService"; private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS = @@ -378,8 +376,6 @@ public final class SystemServer implements Dumpable { "com.android.server.searchui.SearchUiManagerService"; private static final String SMARTSPACE_MANAGER_SERVICE_CLASS = "com.android.server.smartspace.SmartspaceManagerService"; - private static final String CLOUDSEARCH_MANAGER_SERVICE_CLASS = - "com.android.server.cloudsearch.CloudSearchManagerService"; private static final String DEVICE_IDLE_CONTROLLER_CLASS = "com.android.server.DeviceIdleController"; private static final String BLOB_STORE_MANAGER_SERVICE_CLASS = @@ -1886,12 +1882,6 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(SMARTSPACE_MANAGER_SERVICE_CLASS); t.traceEnd(); - // CloudSearch manager service - // TODO: add deviceHasConfigString(context, R.string.config_defaultCloudSearchServices) - t.traceBegin("StartCloudSearchService"); - mSystemServiceManager.startService(CLOUDSEARCH_MANAGER_SERVICE_CLASS); - t.traceEnd(); - t.traceBegin("InitConnectivityModuleConnector"); try { ConnectivityModuleConnector.getInstance().init(context); @@ -2636,11 +2626,6 @@ public final class SystemServer implements Dumpable { Slog.d(TAG, "TranslationService not defined by OEM"); } - // Selection toolbar service - t.traceBegin("StartSelectionToolbarManagerService"); - mSystemServiceManager.startService(SELECTION_TOOLBAR_MANAGER_SERVICE_CLASS); - t.traceEnd(); - // NOTE: ClipboardService depends on ContentCapture and Autofill t.traceBegin("StartClipboardService"); mSystemServiceManager.startService(ClipboardService.class); diff --git a/services/selectiontoolbar/Android.bp b/services/selectiontoolbar/Android.bp deleted file mode 100644 index cc6405f97bc3..000000000000 --- a/services/selectiontoolbar/Android.bp +++ /dev/null @@ -1,22 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -filegroup { - name: "services.selectiontoolbar-sources", - srcs: ["java/**/*.java"], - path: "java", - visibility: ["//frameworks/base/services"], -} - -java_library_static { - name: "services.selectiontoolbar", - defaults: ["platform_service_defaults"], - srcs: [":services.selectiontoolbar-sources"], - libs: ["services.core"], -} diff --git a/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java index c4c3abc1388e..145e66c92f14 100644 --- a/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java @@ -30,10 +30,13 @@ import static org.mockito.Mockito.when; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.UserInfo; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraInjectionSession; import android.hardware.camera2.CameraManager; import android.os.Process; +import android.os.UserManager; import android.testing.TestableContext; import android.util.ArraySet; @@ -51,12 +54,17 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.List; + @RunWith(AndroidJUnit4.class) public class CameraAccessControllerTest { private static final String FRONT_CAMERA = "0"; private static final String REAR_CAMERA = "1"; private static final String TEST_APP_PACKAGE = "some.package"; private static final String OTHER_APP_PACKAGE = "other.package"; + private static final int PERSONAL_PROFILE_USER_ID = 0; + private static final int WORK_PROFILE_USER_ID = 10; private CameraAccessController mController; @@ -69,6 +77,8 @@ public class CameraAccessControllerTest { @Mock private PackageManager mPackageManager; @Mock + private UserManager mUserManager; + @Mock private VirtualDeviceManagerInternal mDeviceManagerInternal; @Mock private CameraAccessController.CameraAccessBlockedCallback mBlockedCallback; @@ -76,6 +86,7 @@ public class CameraAccessControllerTest { private ApplicationInfo mTestAppInfo = new ApplicationInfo(); private ApplicationInfo mOtherAppInfo = new ApplicationInfo(); private ArraySet<Integer> mRunningUids = new ArraySet<>(); + private List<UserInfo> mAliveUsers = new ArrayList<>(); @Captor ArgumentCaptor<CameraInjectionSession.InjectionStatusCallback> mInjectionCallbackCaptor; @@ -84,6 +95,7 @@ public class CameraAccessControllerTest { public void setUp() throws PackageManager.NameNotFoundException { MockitoAnnotations.initMocks(this); mContext.addMockSystemService(CameraManager.class, mCameraManager); + mContext.addMockSystemService(UserManager.class, mUserManager); mContext.setMockPackageManager(mPackageManager); LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class); LocalServices.addService(VirtualDeviceManagerInternal.class, mDeviceManagerInternal); @@ -92,10 +104,14 @@ public class CameraAccessControllerTest { mTestAppInfo.uid = Process.FIRST_APPLICATION_UID; mOtherAppInfo.uid = Process.FIRST_APPLICATION_UID + 1; mRunningUids.add(Process.FIRST_APPLICATION_UID); - when(mPackageManager.getApplicationInfo(eq(TEST_APP_PACKAGE), anyInt())).thenReturn( - mTestAppInfo); - when(mPackageManager.getApplicationInfo(eq(OTHER_APP_PACKAGE), anyInt())).thenReturn( - mOtherAppInfo); + mAliveUsers.add(new UserInfo(PERSONAL_PROFILE_USER_ID, "", 0)); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + anyInt())).thenReturn(mTestAppInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(OTHER_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + anyInt())).thenReturn(mOtherAppInfo); + when(mUserManager.getAliveUsers()).thenReturn(mAliveUsers); mController.startObservingIfNeeded(); } @@ -227,4 +243,74 @@ public class CameraAccessControllerTest { verify(mCameraManager, times(1)).injectCamera(any(), any(), any(), any(), any()); } + + @Test + public void multipleUsers_getPersonalProfileAppUid_cameraBlocked() + throws CameraAccessException, NameNotFoundException { + mAliveUsers.add(new UserInfo(WORK_PROFILE_USER_ID, "", 0)); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(PERSONAL_PROFILE_USER_ID))).thenReturn(mTestAppInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(WORK_PROFILE_USER_ID))).thenThrow(NameNotFoundException.class); + when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice( + eq(mTestAppInfo.uid))).thenReturn(true); + mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE); + + verify(mCameraManager).injectCamera(eq(TEST_APP_PACKAGE), eq(FRONT_CAMERA), anyString(), + any(), any()); + } + + @Test + public void multipleUsers_getPersonalProfileAppUid_noCameraBlocking() + throws CameraAccessException, NameNotFoundException { + mAliveUsers.add(new UserInfo(WORK_PROFILE_USER_ID, "", 0)); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(PERSONAL_PROFILE_USER_ID))).thenReturn(mTestAppInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(WORK_PROFILE_USER_ID))).thenThrow(NameNotFoundException.class); + when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice( + eq(mTestAppInfo.uid))).thenReturn(false); + mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE); + + verify(mCameraManager, never()).injectCamera(any(), any(), any(), any(), any()); + } + + @Test + public void multipleUsers_getWorkProfileAppUid_cameraBlocked() + throws CameraAccessException, NameNotFoundException { + mAliveUsers.add(new UserInfo(WORK_PROFILE_USER_ID, "", 0)); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(PERSONAL_PROFILE_USER_ID))).thenThrow(NameNotFoundException.class); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(WORK_PROFILE_USER_ID))).thenReturn(mTestAppInfo); + when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice( + eq(mTestAppInfo.uid))).thenReturn(true); + mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE); + + verify(mCameraManager).injectCamera(eq(TEST_APP_PACKAGE), eq(FRONT_CAMERA), anyString(), + any(), any()); + } + + @Test + public void multipleUsers_getWorkProfileAppUid_noCameraBlocking() + throws CameraAccessException, NameNotFoundException { + mAliveUsers.add(new UserInfo(WORK_PROFILE_USER_ID, "", 0)); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(PERSONAL_PROFILE_USER_ID))).thenThrow(NameNotFoundException.class); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(WORK_PROFILE_USER_ID))).thenReturn(mTestAppInfo); + when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice( + eq(mTestAppInfo.uid))).thenReturn(false); + mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE); + + verify(mCameraManager, never()).injectCamera(any(), any(), any(), any(), any()); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java index 849e6730ac11..00d7541a79dc 100644 --- a/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java @@ -155,6 +155,27 @@ public class AlarmQueueTest { anyInt(), eq(nowElapsed + HOUR_IN_MILLIS), eq(ALARM_TAG), any(), any()); } + @Test + public void testAddingLargeAlarmTimes() { + final AlarmQueue<String> alarmQueue = createAlarmQueue(true, 0); + final long nowElapsed = mInjector.getElapsedRealtime(); + + InOrder inOrder = inOrder(mAlarmManager); + + alarmQueue.addAlarm("com.android.test.1", Long.MAX_VALUE - 5); + inOrder.verify(mAlarmManager, timeout(1000).times(1)) + .setExact(anyInt(), eq(Long.MAX_VALUE - 5), eq(ALARM_TAG), any(), any()); + alarmQueue.addAlarm("com.android.test.2", Long.MAX_VALUE - 4); + inOrder.verify(mAlarmManager, never()) + .setExact(anyInt(), anyLong(), eq(ALARM_TAG), any(), any()); + alarmQueue.addAlarm("com.android.test.3", nowElapsed + 5); + inOrder.verify(mAlarmManager, timeout(1000).times(1)) + .setExact(anyInt(), eq(nowElapsed + 5), eq(ALARM_TAG), any(), any()); + alarmQueue.addAlarm("com.android.test.4", nowElapsed + 6); + inOrder.verify(mAlarmManager, never()) + .setExact(anyInt(), anyLong(), eq(ALARM_TAG), any(), any()); + } + /** * Verify that updating the alarm time for a key will result in the AlarmManager alarm changing, * if needed. diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java index 038cbc032375..94f88abf7301 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java @@ -587,6 +587,157 @@ public final class DeviceStateManagerServiceTest { }); } + @Test + public void requestBaseStateOverride() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + flushHandler(); + + final IBinder token = new Binder(); + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_UNKNOWN); + + mService.getBinderService().requestBaseStateOverride(token, + OTHER_DEVICE_STATE.getIdentifier(), + 0 /* flags */); + // Flush the handler twice. The first flush ensures the request is added and the policy is + // notified, while the second flush ensures the callback is notified once the change is + // committed. + flushHandler(2 /* count */); + + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_ACTIVE); + // Committed state changes as there is a requested override. + assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE)); + assertEquals(mSysPropSetter.getValue(), + OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName()); + assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE)); + assertEquals(mService.getOverrideBaseState().get(), OTHER_DEVICE_STATE); + assertFalse(mService.getOverrideState().isPresent()); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + OTHER_DEVICE_STATE.getIdentifier()); + + assertNotNull(callback.getLastNotifiedInfo()); + assertEquals(callback.getLastNotifiedInfo().baseState, + OTHER_DEVICE_STATE.getIdentifier()); + assertEquals(callback.getLastNotifiedInfo().currentState, + OTHER_DEVICE_STATE.getIdentifier()); + + mService.getBinderService().cancelBaseStateOverride(); + flushHandler(); + + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_CANCELED); + // Committed state is set back to the requested state once the override is cleared. + assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE)); + assertEquals(mSysPropSetter.getValue(), + DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName()); + assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE)); + assertFalse(mService.getOverrideBaseState().isPresent()); + assertFalse(mService.getOverrideState().isPresent()); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + DEFAULT_DEVICE_STATE.getIdentifier()); + + assertEquals(callback.getLastNotifiedInfo().baseState, + DEFAULT_DEVICE_STATE.getIdentifier()); + assertEquals(callback.getLastNotifiedInfo().currentState, + DEFAULT_DEVICE_STATE.getIdentifier()); + } + + @Test + public void requestBaseStateOverride_cancelledByBaseStateUpdate() throws RemoteException { + final DeviceState testDeviceState = new DeviceState(2, "TEST", 0); + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + mProvider.notifySupportedDeviceStates( + new DeviceState[]{DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE, testDeviceState }); + flushHandler(); + + final IBinder token = new Binder(); + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_UNKNOWN); + + mService.getBinderService().requestBaseStateOverride(token, + OTHER_DEVICE_STATE.getIdentifier(), + 0 /* flags */); + // Flush the handler twice. The first flush ensures the request is added and the policy is + // notified, while the second flush ensures the callback is notified once the change is + // committed. + flushHandler(2 /* count */); + + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_ACTIVE); + // Committed state changes as there is a requested override. + assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE)); + assertEquals(mSysPropSetter.getValue(), + OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName()); + assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE)); + assertEquals(mService.getOverrideBaseState().get(), OTHER_DEVICE_STATE); + assertFalse(mService.getOverrideState().isPresent()); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + OTHER_DEVICE_STATE.getIdentifier()); + + assertNotNull(callback.getLastNotifiedInfo()); + assertEquals(callback.getLastNotifiedInfo().baseState, + OTHER_DEVICE_STATE.getIdentifier()); + assertEquals(callback.getLastNotifiedInfo().currentState, + OTHER_DEVICE_STATE.getIdentifier()); + + mProvider.setState(testDeviceState.getIdentifier()); + flushHandler(); + + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_CANCELED); + // Committed state is set to the new base state once the override is cleared. + assertEquals(mService.getCommittedState(), Optional.of(testDeviceState)); + assertEquals(mSysPropSetter.getValue(), + testDeviceState.getIdentifier() + ":" + testDeviceState.getName()); + assertEquals(mService.getBaseState(), Optional.of(testDeviceState)); + assertFalse(mService.getOverrideBaseState().isPresent()); + assertFalse(mService.getOverrideState().isPresent()); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + testDeviceState.getIdentifier()); + + assertEquals(callback.getLastNotifiedInfo().baseState, + testDeviceState.getIdentifier()); + assertEquals(callback.getLastNotifiedInfo().currentState, + testDeviceState.getIdentifier()); + } + + @Test + public void requestBaseState_unsupportedState() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + + assertThrows(IllegalArgumentException.class, () -> { + final IBinder token = new Binder(); + mService.getBinderService().requestBaseStateOverride(token, + UNSUPPORTED_DEVICE_STATE.getIdentifier(), 0 /* flags */); + }); + } + + @Test + public void requestBaseState_invalidState() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + + assertThrows(IllegalArgumentException.class, () -> { + final IBinder token = new Binder(); + mService.getBinderService().requestBaseStateOverride(token, INVALID_DEVICE_STATE, + 0 /* flags */); + }); + } + + @Test + public void requestBaseState_beforeRegisteringCallback() { + assertThrows(IllegalStateException.class, () -> { + final IBinder token = new Binder(); + mService.getBinderService().requestBaseStateOverride(token, + DEFAULT_DEVICE_STATE.getIdentifier(), + 0 /* flags */); + }); + } + private static void assertArrayEquals(int[] expected, int[] actual) { Assert.assertTrue(Arrays.equals(expected, actual)); } diff --git a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java index 2297c91818c0..430504ca2428 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java @@ -16,6 +16,8 @@ package com.android.server.devicestate; +import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE; +import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE; import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE; import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED; @@ -57,7 +59,7 @@ public final class OverrideRequestControllerTest { @Test public void addRequest() { OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */, - 0 /* requestedState */, 0 /* flags */); + 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); assertNull(mStatusListener.getLastStatus(request)); mController.addRequest(request); @@ -67,14 +69,14 @@ public final class OverrideRequestControllerTest { @Test public void addRequest_cancelExistingRequestThroughNewRequest() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, - 0 /* requestedState */, 0 /* flags */); + 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); assertNull(mStatusListener.getLastStatus(firstRequest)); mController.addRequest(firstRequest); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, - 1 /* requestedState */, 0 /* flags */); + 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); assertNull(mStatusListener.getLastStatus(secondRequest)); mController.addRequest(secondRequest); @@ -85,7 +87,7 @@ public final class OverrideRequestControllerTest { @Test public void addRequest_cancelActiveRequest() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, - 0 /* requestedState */, 0 /* flags */); + 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); mController.addRequest(firstRequest); @@ -97,30 +99,90 @@ public final class OverrideRequestControllerTest { } @Test + public void addBaseStateRequest() { + OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); + assertNull(mStatusListener.getLastStatus(request)); + + mController.addBaseStateRequest(request); + assertEquals(mStatusListener.getLastStatus(request).intValue(), STATUS_ACTIVE); + } + + @Test + public void addBaseStateRequest_cancelExistingBaseStateRequestThroughNewRequest() { + OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); + assertNull(mStatusListener.getLastStatus(firstRequest)); + + mController.addBaseStateRequest(firstRequest); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); + + OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); + assertNull(mStatusListener.getLastStatus(secondRequest)); + + mController.addBaseStateRequest(secondRequest); + assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE); + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED); + } + + @Test + public void addBaseStateRequest_cancelActiveBaseStateRequest() { + OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); + + mController.addBaseStateRequest(firstRequest); + + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); + + mController.cancelBaseStateOverrideRequest(); + + assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED); + } + + @Test public void handleBaseStateChanged() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* requestedState */, - DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */); + DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */, + OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + + OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 0 /* requestedState */, + 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); mController.addRequest(firstRequest); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); - mController.handleBaseStateChanged(); + mController.addBaseStateRequest(baseStateRequest); + + assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE); + + mController.handleBaseStateChanged(1); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED); + assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED); } @Test public void handleProcessDied() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, - 0 /* requestedState */, 0 /* flags */); + 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + + OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 1 /* requestedState */, + 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); mController.addRequest(firstRequest); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); + mController.addBaseStateRequest(baseStateRequest); + assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE); + mController.handleProcessDied(0); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED); + assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED); } @Test @@ -128,13 +190,20 @@ public final class OverrideRequestControllerTest { mController.setStickyRequestsAllowed(true); OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, - 0 /* requestedState */, 0 /* flags */); + 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + + OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); mController.addRequest(firstRequest); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); + mController.addBaseStateRequest(baseStateRequest); + assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE); + mController.handleProcessDied(0); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); + assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED); mController.cancelStickyRequest(); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED); @@ -143,22 +212,31 @@ public final class OverrideRequestControllerTest { @Test public void handleNewSupportedStates() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, - 1 /* requestedState */, 0 /* flags */); + 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + + OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */, + 1 /* requestedState */, + 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); mController.addRequest(firstRequest); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); - mController.handleNewSupportedStates(new int[]{ 0, 1 }); + mController.addBaseStateRequest(baseStateRequest); + assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE); + + mController.handleNewSupportedStates(new int[]{0, 1}); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); + assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE); - mController.handleNewSupportedStates(new int[]{ 0 }); + mController.handleNewSupportedStates(new int[]{0}); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED); + assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED); } @Test public void cancelOverrideRequestsTest() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, - 1 /* requestedState */, 0 /* flags */); + 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); mController.addRequest(firstRequest); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java index f352de4ea54e..89ff2c258c26 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java @@ -48,7 +48,7 @@ import java.util.Arrays; @RunWith(AndroidJUnit4.class) public class BrightnessMappingStrategyTest { - private static final int[] LUX_LEVELS = { + private static final float[] LUX_LEVELS = { 0, 5, 20, @@ -126,7 +126,8 @@ public class BrightnessMappingStrategyTest { private static final int[] EMPTY_INT_ARRAY = new int[0]; private static final float MAXIMUM_GAMMA = 3.0f; - private static final int[] GAMMA_CORRECTION_LUX = { + + private static final float[] GAMMA_CORRECTION_LUX = { 0, 100, 1000, @@ -155,7 +156,7 @@ public class BrightnessMappingStrategyTest { @Test public void testSimpleStrategyMappingAtControlPoints() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); + Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT); DisplayDeviceConfig ddc = createDdc(); BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", simple); @@ -170,7 +171,7 @@ public class BrightnessMappingStrategyTest { @Test public void testSimpleStrategyMappingBetweenControlPoints() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); + Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT); DisplayDeviceConfig ddc = createDdc(); BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", simple); @@ -179,13 +180,13 @@ public class BrightnessMappingStrategyTest { final float backlight = simple.getBrightness(lux) * PowerManager.BRIGHTNESS_ON; assertTrue("Desired brightness should be between adjacent control points.", backlight > DISPLAY_LEVELS_BACKLIGHT[i - 1] - && backlight < DISPLAY_LEVELS_BACKLIGHT[i]); + && backlight < DISPLAY_LEVELS_BACKLIGHT[i]); } } @Test public void testSimpleStrategyIgnoresNewConfiguration() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); + Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT); DisplayDeviceConfig ddc = createDdc(); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); @@ -200,7 +201,7 @@ public class BrightnessMappingStrategyTest { @Test public void testSimpleStrategyIgnoresNullConfiguration() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); + Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT); DisplayDeviceConfig ddc = createDdc(); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); @@ -214,8 +215,10 @@ public class BrightnessMappingStrategyTest { @Test public void testPhysicalStrategyMappingAtControlPoints() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); - DisplayDeviceConfig ddc = createDdc(); + Resources res = createResources(EMPTY_INT_ARRAY); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, + LUX_LEVELS, DISPLAY_LEVELS_NITS); BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", physical); for (int i = 0; i < LUX_LEVELS.length; i++) { @@ -231,8 +234,9 @@ public class BrightnessMappingStrategyTest { @Test public void testPhysicalStrategyMappingBetweenControlPoints() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); - DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE); + Resources res = createResources(EMPTY_INT_ARRAY); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE, + LUX_LEVELS, DISPLAY_LEVELS_NITS); BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", physical); Spline brightnessToNits = @@ -248,11 +252,12 @@ public class BrightnessMappingStrategyTest { @Test public void testPhysicalStrategyUsesNewConfigurations() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); - DisplayDeviceConfig ddc = createDdc(); + Resources res = createResources(EMPTY_INT_ARRAY); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); - final float[] lux = { 0f, 1f }; + final float[] lux = {0f, 1f}; final float[] nits = { DISPLAY_RANGE_NITS[0], DISPLAY_RANGE_NITS[DISPLAY_RANGE_NITS.length - 1] @@ -273,8 +278,9 @@ public class BrightnessMappingStrategyTest { @Test public void testPhysicalStrategyRecalculateSplines() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); - DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS); + Resources res = createResources(EMPTY_INT_ARRAY); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length]; for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) { @@ -304,28 +310,30 @@ public class BrightnessMappingStrategyTest { @Test public void testDefaultStrategyIsPhysical() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT, - DISPLAY_LEVELS_NITS); - DisplayDeviceConfig ddc = createDdc(); + Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy); } @Test public void testNonStrictlyIncreasingLuxLevelsFails() { - final int[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length); + final float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length); final int idx = lux.length / 2; - int tmp = lux[idx]; - lux[idx] = lux[idx+1]; - lux[idx+1] = tmp; - Resources res = createResources(lux, DISPLAY_LEVELS_NITS); - DisplayDeviceConfig ddc = createDdc(); + float tmp = lux[idx]; + lux[idx] = lux[idx + 1]; + lux[idx + 1] = tmp; + Resources res = createResources(EMPTY_INT_ARRAY); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, lux, DISPLAY_LEVELS_NITS); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(strategy); // And make sure we get the same result even if it's monotone but not increasing. - lux[idx] = lux[idx+1]; - res = createResources(lux, DISPLAY_LEVELS_NITS); + lux[idx] = lux[idx + 1]; + ddc = createDdc(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, lux, + DISPLAY_LEVELS_NITS); strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(strategy); } @@ -333,62 +341,64 @@ public class BrightnessMappingStrategyTest { @Test public void testDifferentNumberOfControlPointValuesFails() { //Extra lux level - final int[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length+1); + final float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length + 1); // Make sure it's strictly increasing so that the only failure is the differing array // lengths lux[lux.length - 1] = lux[lux.length - 2] + 1; - Resources res = createResources(lux, DISPLAY_LEVELS_NITS); - DisplayDeviceConfig ddc = createDdc(); + Resources res = createResources(EMPTY_INT_ARRAY); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, lux, DISPLAY_LEVELS_NITS); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(strategy); - res = createResources(lux, DISPLAY_LEVELS_BACKLIGHT); + res = createResources(DISPLAY_LEVELS_BACKLIGHT); strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(strategy); // Extra backlight level final int[] backlight = Arrays.copyOf( - DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1); + DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length + 1); backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1; - res = createResources(LUX_LEVELS, backlight); + res = createResources(backlight); + ddc = createDdc(DISPLAY_RANGE_NITS, + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, EMPTY_FLOAT_ARRAY); strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(strategy); // Extra nits level - final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1); + final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length + 1); nits[nits.length - 1] = nits[nits.length - 2] + 1; - res = createResources(LUX_LEVELS, nits); + res = createResources(EMPTY_INT_ARRAY); + ddc = createDdc(DISPLAY_RANGE_NITS, + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, nits); strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(strategy); } @Test public void testPhysicalStrategyRequiresNitsMapping() { - Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS); + Resources res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/); DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY /*nitsRange*/); BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(physical); - res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS); + res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/); physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(physical); - res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS); + res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/); physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(physical); } @Test public void testStrategiesAdaptToUserDataPoint() { - Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS); - DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE); + Resources res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE, + LUX_LEVELS, DISPLAY_LEVELS_NITS); assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc)); ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE); - res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); + res = createResources(DISPLAY_LEVELS_BACKLIGHT); assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc)); } @@ -468,42 +478,19 @@ public class BrightnessMappingStrategyTest { assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.0001f /*tolerance*/); } - private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight) { - return createResources(luxLevels, brightnessLevelsBacklight, - EMPTY_FLOAT_ARRAY /*brightnessLevelsNits*/); - } - - private Resources createResources(int[] luxLevels, float[] brightnessLevelsNits) { - return createResources(luxLevels, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, - brightnessLevelsNits); + private Resources createResources(int[] brightnessLevelsBacklight) { + return createResources(brightnessLevelsBacklight, EMPTY_INT_ARRAY, EMPTY_FLOAT_ARRAY); } - private Resources createResourcesIdle(int[] luxLevels, float[] brightnessLevelsNits) { - return createResources(EMPTY_INT_ARRAY, EMPTY_INT_ARRAY, EMPTY_FLOAT_ARRAY, - luxLevels, brightnessLevelsNits); + private Resources createResourcesIdle(int[] luxLevelsIdle, float[] brightnessLevelsNitsIdle) { + return createResources(EMPTY_INT_ARRAY, + luxLevelsIdle, brightnessLevelsNitsIdle); } - private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight, - float[] brightnessLevelsNits) { - return createResources(luxLevels, brightnessLevelsBacklight, brightnessLevelsNits, - EMPTY_INT_ARRAY, EMPTY_FLOAT_ARRAY); - - } - - private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight, - float[] brightnessLevelsNits, int[] luxLevelsIdle, float[] brightnessLevelsNitsIdle) { + private Resources createResources(int[] brightnessLevelsBacklight, int[] luxLevelsIdle, + float[] brightnessLevelsNitsIdle) { Resources mockResources = mock(Resources.class); - - // For historical reasons, the lux levels resource implicitly defines the first point as 0, - // so we need to chop it off of the array the mock resource object returns. - // Don't mock if these values are not set. If we try to use them, we will fail. - if (luxLevels.length > 0) { - int[] luxLevelsResource = Arrays.copyOfRange(luxLevels, 1, luxLevels.length); - when(mockResources.getIntArray( - com.android.internal.R.array.config_autoBrightnessLevels)) - .thenReturn(luxLevelsResource); - } if (luxLevelsIdle.length > 0) { int[] luxLevelsIdleResource = Arrays.copyOfRange(luxLevelsIdle, 1, luxLevelsIdle.length); @@ -516,10 +503,6 @@ public class BrightnessMappingStrategyTest { com.android.internal.R.array.config_autoBrightnessLcdBacklightValues)) .thenReturn(brightnessLevelsBacklight); - TypedArray mockBrightnessLevelNits = createFloatTypedArray(brightnessLevelsNits); - when(mockResources.obtainTypedArray( - com.android.internal.R.array.config_autoBrightnessDisplayValuesNits)) - .thenReturn(mockBrightnessLevelNits); TypedArray mockBrightnessLevelNitsIdle = createFloatTypedArray(brightnessLevelsNitsIdle); when(mockResources.obtainTypedArray( com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle)) @@ -549,6 +532,18 @@ public class BrightnessMappingStrategyTest { DisplayDeviceConfig mockDdc = mock(DisplayDeviceConfig.class); when(mockDdc.getNits()).thenReturn(nitsArray); when(mockDdc.getBrightness()).thenReturn(backlightArray); + when(mockDdc.getAutoBrightnessBrighteningLevelsLux()).thenReturn(LUX_LEVELS); + when(mockDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(EMPTY_FLOAT_ARRAY); + return mockDdc; + } + + private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray, + float[] luxLevelsFloat, float[] brightnessLevelsNits) { + DisplayDeviceConfig mockDdc = mock(DisplayDeviceConfig.class); + when(mockDdc.getNits()).thenReturn(nitsArray); + when(mockDdc.getBrightness()).thenReturn(backlightArray); + when(mockDdc.getAutoBrightnessBrighteningLevelsLux()).thenReturn(luxLevelsFloat); + when(mockDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(brightnessLevelsNits); return mockDdc; } @@ -590,8 +585,10 @@ public class BrightnessMappingStrategyTest { final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3); - Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); - DisplayDeviceConfig ddc = createDdc(); + Resources resources = createResources(EMPTY_INT_ARRAY); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX, + GAMMA_CORRECTION_NITS); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, mMockDwbc); // Let's start with a validity check: @@ -619,8 +616,10 @@ public class BrightnessMappingStrategyTest { final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1); final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3); - Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); - DisplayDeviceConfig ddc = createDdc(); + Resources resources = createResources(EMPTY_INT_ARRAY); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX, + GAMMA_CORRECTION_NITS); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, mMockDwbc); // Validity check: @@ -645,8 +644,10 @@ public class BrightnessMappingStrategyTest { public void testGammaCorrectionExtremeChangeAtCenter() { // Extreme changes (e.g. setting brightness to 0.0 or 1.0) can't be gamma corrected, so we // just make sure the adjustment reflects the change. - Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); - DisplayDeviceConfig ddc = createDdc(); + Resources resources = createResources(EMPTY_INT_ARRAY); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX, + GAMMA_CORRECTION_NITS); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, mMockDwbc); assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); @@ -667,8 +668,10 @@ public class BrightnessMappingStrategyTest { final float y0 = GAMMA_CORRECTION_SPLINE.interpolate(x0); final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); final float y4 = GAMMA_CORRECTION_SPLINE.interpolate(x4); - Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); - DisplayDeviceConfig ddc = createDdc(); + Resources resources = createResources(EMPTY_INT_ARRAY); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX, + GAMMA_CORRECTION_NITS); BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, mMockDwbc); // Validity, as per tradition: diff --git a/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java index f33001774263..2cd5314e961f 100644 --- a/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java @@ -16,6 +16,8 @@ package com.android.server.logcat; +import static android.os.Process.INVALID_UID; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -28,6 +30,7 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.content.ContextWrapper; +import android.content.pm.PackageManager; import android.os.ILogd; import android.os.Looper; import android.os.UserHandle; @@ -69,10 +72,12 @@ public class LogcatManagerServiceTest { @Mock private ActivityManagerInternal mActivityManagerInternalMock; @Mock + private PackageManager mPackageManagerMock; + @Mock private ILogd mLogdMock; private LogcatManagerService mService; - private LogcatManagerService.LogcatManagerServiceInternal mLocalService; + private LogcatManagerService.LogAccessDialogCallback mDialogCallback; private ContextWrapper mContextSpy; private OffsettableClock mClock; private TestLooper mTestLooper; @@ -81,10 +86,17 @@ public class LogcatManagerServiceTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock); + when(mActivityManagerInternalMock.getInstrumentationSourceUid(anyInt())) + .thenReturn(INVALID_UID); + mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); mClock = new OffsettableClock.Stopped(); mTestLooper = new TestLooper(mClock::now); - + when(mContextSpy.getPackageManager()).thenReturn(mPackageManagerMock); + when(mPackageManagerMock.getPackagesForUid(APP1_UID)).thenReturn( + new String[]{APP1_PACKAGE_NAME}); + when(mPackageManagerMock.getPackagesForUid(APP2_UID)).thenReturn( + new String[]{APP2_PACKAGE_NAME}); when(mActivityManagerInternalMock.getPackageNameByPid(APP1_PID)).thenReturn( APP1_PACKAGE_NAME); when(mActivityManagerInternalMock.getPackageNameByPid(APP2_PID)).thenReturn( @@ -106,7 +118,7 @@ public class LogcatManagerServiceTest { return mLogdMock; } }); - mLocalService = mService.getLocalService(); + mDialogCallback = mService.getDialogCallback(); mService.onStart(); } @@ -136,6 +148,20 @@ public class LogcatManagerServiceTest { } @Test + public void test_RequestFromBackground_ApprovedIfInstrumented() throws Exception { + when(mActivityManagerInternalMock.getInstrumentationSourceUid(APP1_UID)) + .thenReturn(APP1_UID); + when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( + ActivityManager.PROCESS_STATE_RECEIVER); + mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); + mTestLooper.dispatchAll(); + + verify(mLogdMock).approve(APP1_UID, APP1_GID, APP1_PID, FD1); + verify(mLogdMock, never()).decline(APP1_UID, APP1_GID, APP1_PID, FD1); + verify(mContextSpy, never()).startActivityAsUser(any(), any()); + } + + @Test public void test_RequestFromForegroundService_DeclinedWithoutPrompt() throws Exception { when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); @@ -181,7 +207,7 @@ public class LogcatManagerServiceTest { mTestLooper.dispatchAll(); verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); - mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); verify(mLogdMock, times(1)).approve(APP1_UID, APP1_GID, APP1_PID, FD1); @@ -196,7 +222,7 @@ public class LogcatManagerServiceTest { mTestLooper.dispatchAll(); verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); - mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); verify(mLogdMock, never()).approve(APP1_UID, APP1_GID, APP1_PID, FD1); @@ -214,7 +240,7 @@ public class LogcatManagerServiceTest { verify(mLogdMock, never()).approve(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); verify(mLogdMock, never()).decline(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); - mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); verify(mLogdMock, times(1)).approve(APP1_UID, APP1_GID, APP1_PID, FD1); @@ -234,7 +260,7 @@ public class LogcatManagerServiceTest { verify(mLogdMock, never()).approve(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); verify(mLogdMock, never()).decline(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); - mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); verify(mLogdMock, times(1)).decline(APP1_UID, APP1_GID, APP1_PID, FD1); @@ -249,7 +275,7 @@ public class LogcatManagerServiceTest { ActivityManager.PROCESS_STATE_TOP); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); mTestLooper.dispatchAll(); - mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD2); @@ -267,7 +293,7 @@ public class LogcatManagerServiceTest { ActivityManager.PROCESS_STATE_TOP); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); mTestLooper.dispatchAll(); - mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD2); @@ -287,7 +313,7 @@ public class LogcatManagerServiceTest { ActivityManager.PROCESS_STATE_TOP); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); mTestLooper.dispatchAll(); - mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); mService.getBinderService().startThread(APP2_UID, APP2_GID, APP2_PID, FD2); @@ -304,7 +330,7 @@ public class LogcatManagerServiceTest { ActivityManager.PROCESS_STATE_TOP); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); mTestLooper.dispatchAll(); - mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); advanceTime(LogcatManagerService.STATUS_EXPIRATION_TIMEOUT_MILLIS); diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java index 7fbf6bb683bf..f6d53852f9a1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java @@ -289,6 +289,14 @@ public class ContentRecorderTests extends WindowTestsBase { } @Test + public void testRemoveTask_stopsRecording_nullSessionShouldNotThrowExceptions() { + mContentRecorder.setContentRecordingSession(mTaskSession); + mContentRecorder.updateRecording(); + mContentRecorder.setContentRecordingSession(null); + mTask.removeImmediately(); + } + + @Test public void testUpdateMirroredSurface_capturedAreaResized() { mContentRecorder.setContentRecordingSession(mDisplaySession); mContentRecorder.updateRecording(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 61cf8cc76d83..1404de253476 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -730,6 +730,16 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } @Test + public void testApplyTransaction_finishActivity() { + final ActivityRecord activity = createActivityRecord(mDisplayContent); + + mTransaction.finishActivity(activity.token); + assertApplyTransactionAllowed(mTransaction); + + assertTrue(activity.finishing); + } + + @Test public void testApplyTransaction_skipTransactionForUnregisterOrganizer() { mController.unregisterOrganizer(mIOrganizer); final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent); diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 8cf32baa49eb..45d8e226c64f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -34,6 +34,7 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.window.TransitionInfo.FLAG_FILLS_TASK; import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY; +import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; @@ -1074,6 +1075,39 @@ public class TransitionTests extends WindowTestsBase { } @Test + public void testIsBehindStartingWindowChange() { + final Transition transition = createTestTransition(TRANSIT_OPEN); + final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; + final ArraySet<WindowContainer> participants = transition.mParticipants; + + final Task task = createTask(mDisplayContent); + final ActivityRecord activity0 = createActivityRecord(task); + final ActivityRecord activity1 = createActivityRecord(task); + doReturn(true).when(activity1).hasStartingWindow(); + + // Start states. + changes.put(activity0, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); + changes.put(activity1, new Transition.ChangeInfo(false /* vis */, false /* exChg */)); + // End states. + activity0.mVisibleRequested = false; + activity1.mVisibleRequested = true; + + participants.add(activity0); + participants.add(activity1); + final ArrayList<WindowContainer> targets = Transition.calculateTargets( + participants, changes); + final TransitionInfo info = Transition.calculateTransitionInfo( + transition.mType, 0 /* flags */, targets, changes, mMockT); + + // All windows in the Task should have FLAG_IS_BEHIND_STARTING_WINDOW because the starting + // window should cover the whole Task. + assertEquals(2, info.getChanges().size()); + assertTrue(info.getChanges().get(0).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW)); + assertTrue(info.getChanges().get(1).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW)); + + } + + @Test public void testFlagInTaskWithEmbeddedActivity() { final Transition transition = createTestTransition(TRANSIT_OPEN); final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; @@ -1120,7 +1154,7 @@ public class TransitionTests extends WindowTestsBase { } @Test - public void testFlagFillsTask() { + public void testFlagFillsTask_embeddingNotFillingTask() { final Transition transition = createTestTransition(TRANSIT_OPEN); final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; final ArraySet<WindowContainer> participants = transition.mParticipants; @@ -1165,6 +1199,67 @@ public class TransitionTests extends WindowTestsBase { } @Test + public void testFlagFillsTask_openActivityFillingTask() { + final Transition transition = createTestTransition(TRANSIT_OPEN); + final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; + final ArraySet<WindowContainer> participants = transition.mParticipants; + + final Task task = createTask(mDisplayContent); + // Set to multi-windowing mode in order to set bounds. + task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + final Rect taskBounds = new Rect(0, 0, 500, 1000); + task.setBounds(taskBounds); + final ActivityRecord activity = createActivityRecord(task); + // Start states: set bounds to make sure the start bounds is ignored if it is not visible. + activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500)); + activity.mVisibleRequested = false; + changes.put(activity, new Transition.ChangeInfo(activity)); + // End states: reset bounds to fill Task. + activity.getConfiguration().windowConfiguration.setBounds(taskBounds); + activity.mVisibleRequested = true; + + participants.add(activity); + final ArrayList<WindowContainer> targets = Transition.calculateTargets( + participants, changes); + final TransitionInfo info = Transition.calculateTransitionInfo( + transition.mType, 0 /* flags */, targets, changes, mMockT); + + // Opening activity that is filling Task after transition should have the flag. + assertEquals(1, info.getChanges().size()); + assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK)); + } + + @Test + public void testFlagFillsTask_closeActivityFillingTask() { + final Transition transition = createTestTransition(TRANSIT_CLOSE); + final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; + final ArraySet<WindowContainer> participants = transition.mParticipants; + + final Task task = createTask(mDisplayContent); + // Set to multi-windowing mode in order to set bounds. + task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + final Rect taskBounds = new Rect(0, 0, 500, 1000); + task.setBounds(taskBounds); + final ActivityRecord activity = createActivityRecord(task); + // Start states: fills Task without override. + activity.mVisibleRequested = true; + changes.put(activity, new Transition.ChangeInfo(activity)); + // End states: set bounds to make sure the start bounds is ignored if it is not visible. + activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500)); + activity.mVisibleRequested = false; + + participants.add(activity); + final ArrayList<WindowContainer> targets = Transition.calculateTargets( + participants, changes); + final TransitionInfo info = Transition.calculateTransitionInfo( + transition.mType, 0 /* flags */, targets, changes, mMockT); + + // Closing activity that is filling Task before transition should have the flag. + assertEquals(1, info.getChanges().size()); + assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK)); + } + + @Test public void testIncludeEmbeddedActivityReparent() { final Transition transition = createTestTransition(TRANSIT_OPEN); final Task task = createTask(mDisplayContent); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index f595c3de104e..ea40100227c4 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -221,6 +221,7 @@ public class UsageStatsService extends SystemService implements final SparseArray<ActivityData> mVisibleActivities = new SparseArray(); @GuardedBy("mLock") private final SparseArray<LaunchTimeAlarmQueue> mLaunchTimeAlarmQueues = new SparseArray<>(); + @GuardedBy("mUsageEventListeners") // Don't hold the main lock when calling out private final ArraySet<UsageStatsManagerInternal.UsageEventListener> mUsageEventListeners = new ArraySet<>(); private final CopyOnWriteArraySet<UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener> @@ -1168,9 +1169,11 @@ public class UsageStatsService extends SystemService implements service.reportEvent(event); } - final int size = mUsageEventListeners.size(); - for (int i = 0; i < size; ++i) { - mUsageEventListeners.valueAt(i).onUsageEvent(userId, event); + synchronized (mUsageEventListeners) { + final int size = mUsageEventListeners.size(); + for (int i = 0; i < size; ++i) { + mUsageEventListeners.valueAt(i).onUsageEvent(userId, event); + } } } @@ -1661,7 +1664,7 @@ public class UsageStatsService extends SystemService implements * Called via the local interface. */ private void registerListener(@NonNull UsageStatsManagerInternal.UsageEventListener listener) { - synchronized (mLock) { + synchronized (mUsageEventListeners) { mUsageEventListeners.add(listener); } } @@ -1671,7 +1674,7 @@ public class UsageStatsService extends SystemService implements */ private void unregisterListener( @NonNull UsageStatsManagerInternal.UsageEventListener listener) { - synchronized (mLock) { + synchronized (mUsageEventListeners) { mUsageEventListeners.remove(listener); } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index bde9c3dfd641..a6e1a3256cb6 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -22,6 +22,7 @@ import static android.service.attention.AttentionService.PROXIMITY_UNKNOWN; import static android.service.voice.HotwordDetectedResult.EXTRA_PROXIMITY_METERS; import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL; import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPHONE; +import static android.service.voice.HotwordDetectionService.ENABLE_PROXIMITY_RESULT; import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_SUCCESS; import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_UNKNOWN; import static android.service.voice.HotwordDetectionService.KEY_INITIALIZATION_STATUS; @@ -185,7 +186,7 @@ final class HotwordDetectionConnection { final int mUser; final Context mContext; - @Nullable final AttentionManagerInternal mAttentionManagerInternal; + @Nullable AttentionManagerInternal mAttentionManagerInternal = null; final AttentionManagerInternal.ProximityUpdateCallbackInternal mProximityCallbackInternal = this::setProximityMeters; @@ -240,9 +241,11 @@ final class HotwordDetectionConnection { mServiceConnectionFactory = new ServiceConnectionFactory(intent, bindInstantServiceAllowed); mRemoteHotwordDetectionService = mServiceConnectionFactory.createLocked(); - mAttentionManagerInternal = LocalServices.getService(AttentionManagerInternal.class); - if (mAttentionManagerInternal != null) { - mAttentionManagerInternal.onStartProximityUpdates(mProximityCallbackInternal); + if (ENABLE_PROXIMITY_RESULT) { + mAttentionManagerInternal = LocalServices.getService(AttentionManagerInternal.class); + if (mAttentionManagerInternal != null) { + mAttentionManagerInternal.onStartProximityUpdates(mProximityCallbackInternal); + } } mLastRestartInstant = Instant.now(); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 0ce0265c3dc5..3da47110fb49 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -95,6 +95,7 @@ import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; +import com.android.internal.util.LatencyTracker; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -191,6 +192,8 @@ public class VoiceInteractionManagerService extends SystemService { mSoundTriggerInternal = LocalServices.getService(SoundTriggerInternal.class); } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { mServiceStub.systemRunning(isSafeMode()); + } else if (phase == PHASE_BOOT_COMPLETED) { + mServiceStub.registerVoiceInteractionSessionListener(mLatencyLoggingListener); } } @@ -2334,4 +2337,36 @@ public class VoiceInteractionManagerService extends SystemService { } }; } + + /** + * End the latency tracking log for keyphrase hotword invocation. + * The measurement covers from when the SoundTrigger HAL emits an event, captured in + * {@link com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging} + * to when the {@link android.service.voice.VoiceInteractionSession} system UI view is shown. + */ + private final IVoiceInteractionSessionListener mLatencyLoggingListener = + new IVoiceInteractionSessionListener.Stub() { + @Override + public void onVoiceSessionShown() throws RemoteException {} + + @Override + public void onVoiceSessionHidden() throws RemoteException {} + + @Override + public void onVoiceSessionWindowVisibilityChanged(boolean visible) + throws RemoteException { + if (visible) { + LatencyTracker.getInstance(mContext) + .onActionEnd(LatencyTracker.ACTION_SHOW_VOICE_INTERACTION); + } + } + + @Override + public void onSetUiHints(Bundle args) throws RemoteException {} + + @Override + public IBinder asBinder() { + return mServiceStub; + } + }; } diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java index c75de42707a6..ac892da765e7 100644 --- a/telephony/java/android/telephony/NetworkService.java +++ b/telephony/java/android/telephony/NetworkService.java @@ -265,7 +265,7 @@ public abstract class NetworkService extends Service { /** @hide */ @Override public void onDestroy() { - mHandlerThread.quit(); + mHandlerThread.quitSafely(); super.onDestroy(); } diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java index 700d61597d00..d8b2cbebdf28 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -725,7 +725,7 @@ public abstract class DataService extends Service { @Override public void onDestroy() { - mHandlerThread.quit(); + mHandlerThread.quitSafely(); super.onDestroy(); } |