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

Commit 832d6096 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

Lock screen preview.

Adds code on the system UI side that renders a preview of the lock
screen. This is done through the content provider, which receives a
handle to a SurfaceView from wallpaper picker, where the UI is actually
shown to the user.

There are several changes here, following the path of the code, they
are:
1. KeyguardQuickAffordanceProvider can handle the "call" from wallpaper
   picker and delegates the rendering to the preview system
2. The preview system starts with KeyguardRemotePreviewManager which is
   responsible for driving the lifecycle of the rendering as the
   connection to the remote SurfaceView is established or torn down
3. The preview system continues with the KeyguardPreviewRenderer which
   builds the view hierarchy and hooks up each view to its appropriate
   view-binder or view-controller
4. For quick affordances, we are making a couple of minor changes to the
   view-binder, view-model, and interactor to allow for them to run in
   "preview" mode where different rules apply: no clicks allowed, the
   affordances are ever-visible (as opposed to only when the lock screen
   is shown), etc.

Bug: 261362750
Test: unit tests created/updated, manually verified in the shortcuts
settings screen

Change-Id: I5d1c51dacbedf0a7a1fb3ec742244141a2fe8a32
parent a5ad61f2
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
/*
 * 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.quickaffordance.shared.model

object KeyguardQuickAffordancePreviewConstants {
    const val MESSAGE_ID_SLOT_SELECTED = 1337
    const val KEY_SLOT_ID = "slot_id"
    const val KEY_INITIALLY_SELECTED_SLOT_ID = "initially_selected_slot_id"
}
+49 −9
Original line number Diff line number Diff line
@@ -16,13 +16,53 @@
* limitations under the License.
*/
-->
<shape
<selector
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
    android:shape="rectangle">
    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">

  <item android:state_selected="true">
    <layer-list>
      <item
          android:left="3dp"
          android:top="3dp"
          android:right="3dp"
          android:bottom="3dp">
        <shape android:shape="oval">
          <solid android:color="?androidprv:attr/colorSurface"/>
          <size
              android:width="@dimen/keyguard_affordance_width"
              android:height="@dimen/keyguard_affordance_height"/>
  <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
        </shape>
      </item>

      <item>
        <shape android:shape="oval">
          <stroke
              android:color="@color/control_primary_text"
              android:width="2dp"/>
          <size
              android:width="@dimen/keyguard_affordance_width"
              android:height="@dimen/keyguard_affordance_height"/>
        </shape>
      </item>
    </layer-list>
  </item>

  <item>
    <layer-list>
      <item
          android:left="3dp"
          android:top="3dp"
          android:right="3dp"
          android:bottom="3dp">
        <shape android:shape="oval">
          <solid android:color="?androidprv:attr/colorSurface"/>
          <size
              android:width="@dimen/keyguard_affordance_width"
              android:height="@dimen/keyguard_affordance_height"/>
        </shape>
      </item>
    </layer-list>
  </item>

</selector>
+18 −11
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;

@@ -43,6 +44,21 @@ public class KeyguardClockSwitch extends RelativeLayout {
    public static final int LARGE = 0;
    public static final int SMALL = 1;

    /** Returns a region for the large clock to position itself, based on the given parent. */
    public static Rect getLargeClockRegion(ViewGroup parent) {
        int largeClockTopMargin = parent.getResources()
                .getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin);
        int targetHeight = parent.getResources()
                .getDimensionPixelSize(R.dimen.large_clock_text_size) * 2;
        int top = parent.getHeight() / 2 - targetHeight / 2
                + largeClockTopMargin / 2;
        return new Rect(
                parent.getLeft(),
                top,
                parent.getRight(),
                top + targetHeight);
    }

    /**
     * Frame for small/large clocks
     */
@@ -129,17 +145,8 @@ public class KeyguardClockSwitch extends RelativeLayout {
            }

            if (mLargeClockFrame.isLaidOut()) {
                int largeClockTopMargin = getResources()
                        .getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin);
                int targetHeight = getResources()
                        .getDimensionPixelSize(R.dimen.large_clock_text_size) * 2;
                int top = mLargeClockFrame.getHeight() / 2 - targetHeight / 2
                        + largeClockTopMargin / 2;
                mClock.getLargeClock().getEvents().onTargetRegionChanged(new Rect(
                        mLargeClockFrame.getLeft(),
                        top,
                        mLargeClockFrame.getRight(),
                        top + targetHeight));
                mClock.getLargeClock().getEvents().onTargetRegionChanged(
                        getLargeClockRegion(mLargeClockFrame));
            }
        }
    }
+20 −0
Original line number Diff line number Diff line
@@ -21,14 +21,18 @@ import android.content.ContentProvider
import android.content.ContentValues
import android.content.Context
import android.content.UriMatcher
import android.content.pm.PackageManager
import android.content.pm.ProviderInfo
import android.database.Cursor
import android.database.MatrixCursor
import android.net.Uri
import android.os.Binder
import android.os.Bundle
import android.util.Log
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
import javax.inject.Inject
import kotlinx.coroutines.runBlocking
@@ -37,6 +41,7 @@ class KeyguardQuickAffordanceProvider :
    ContentProvider(), SystemUIAppComponentFactoryBase.ContextInitializer {

    @Inject lateinit var interactor: KeyguardQuickAffordanceInteractor
    @Inject lateinit var previewManager: KeyguardRemotePreviewManager

    private lateinit var contextAvailableCallback: ContextAvailableCallback

@@ -149,6 +154,21 @@ class KeyguardQuickAffordanceProvider :
        return deleteSelection(uri, selectionArgs)
    }

    override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
        return if (
            requireContext()
                .checkPermission(
                    android.Manifest.permission.BIND_WALLPAPER,
                    Binder.getCallingPid(),
                    Binder.getCallingUid(),
                ) == PackageManager.PERMISSION_GRANTED
        ) {
            previewManager.preview(extras)
        } else {
            null
        }
    }

    private fun insertSelection(values: ContentValues?): Uri? {
        if (values == null) {
            throw IllegalArgumentException("Cannot insert selection, no values passed in!")
+14 −11
Original line number Diff line number Diff line
@@ -34,7 +34,6 @@ import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentati
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
@@ -67,7 +66,7 @@ constructor(
        position: KeyguardQuickAffordancePosition
    ): Flow<KeyguardQuickAffordanceModel> {
        return combine(
            quickAffordanceInternal(position),
            quickAffordanceAlwaysVisible(position),
            keyguardInteractor.isDozing,
            keyguardInteractor.isKeyguardShowing,
        ) { affordance, isDozing, isKeyguardShowing ->
@@ -79,6 +78,19 @@ constructor(
        }
    }

    /**
     * Returns an observable for the quick affordance at the given position but always visible,
     * regardless of lock screen state.
     *
     * This is useful for experiences like the lock screen preview mode, where the affordances must
     * always be visible.
     */
    fun quickAffordanceAlwaysVisible(
        position: KeyguardQuickAffordancePosition,
    ): Flow<KeyguardQuickAffordanceModel> {
        return quickAffordanceInternal(position)
    }

    /**
     * Notifies that a quick affordance has been "triggered" (clicked) by the user.
     *
@@ -290,15 +302,6 @@ constructor(
        }
    }

    private fun KeyguardQuickAffordancePosition.toSlotId(): String {
        return when (this) {
            KeyguardQuickAffordancePosition.BOTTOM_START ->
                KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
            KeyguardQuickAffordancePosition.BOTTOM_END ->
                KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END
        }
    }

    private fun String.encode(slotId: String): String {
        return "$slotId$DELIMITER$this"
    }
Loading