Odkryj funkcje kamery

1. Zanim zaczniesz

Co jest wyjątkowego w telefonach składanych?

Urządzenia składane to innowacje, które pojawiają się raz na pokolenie. Zapewniają one wyjątkowe wrażenia, a tym samym wyjątkowe możliwości zachwycenia użytkowników wyróżniającymi się funkcjami, takimi jak interfejs stołowy do obsługi bez użycia rąk.

Wymagania wstępne

  • Podstawowa wiedza na temat tworzenia aplikacji na Androida
  • Podstawowa wiedza o platformie wstrzykiwania zależności Hilt

Co utworzysz

W tym module utworzysz aplikację aparatu ze zoptymalizowanymi układami na urządzenia składane.

6caebc2739522a1b.png

Zaczynasz od podstawowej aplikacji aparatu, która nie reaguje na żadną pozycję urządzenia ani nie wykorzystuje lepszego tylnego aparatu do robienia lepszych selfie. Aktualizujesz kod źródłowy, aby przenieść podgląd na mniejszy wyświetlacz po rozłożeniu urządzenia i reagować na ustawienie telefonu w trybie na stole.

Aplikacja aparatu to najwygodniejszy przypadek użycia tego interfejsu API, ale obie funkcje, których nauczysz się w tym laboratorium, można zastosować w dowolnej aplikacji.

Czego się nauczysz

  • Jak używać Jetpack Window Manager do reagowania na zmiany pozycji
  • Jak przenieść aplikację na mniejszy wyświetlacz urządzenia składanego

Czego potrzebujesz

  • Najnowsza wersja Androida Studio
  • składane urządzenie lub emulator składanego urządzenia;

2. Konfiguracja

Pobieranie kodu początkowego

  1. Jeśli masz zainstalowany Git, możesz po prostu uruchomić to polecenie. Aby sprawdzić, czy Git jest zainstalowany, wpisz git --version w terminalu lub wierszu poleceń i sprawdź, czy polecenie zostało wykonane prawidłowo.
git clone https://github.com/android/large-screen-codelabs.git
  1. Opcjonalnie: jeśli nie masz Gita, możesz kliknąć ten przycisk, aby pobrać cały kod do tego ćwiczenia:

Otwórz pierwszy moduł

  • W Android Studio otwórz pierwszy moduł w sekcji /step1.

Zrzut ekranu z Android Studio przedstawiający kod związany z tym ćwiczeniem

Jeśli pojawi się prośba o użycie najnowszej wersji Gradle, zaktualizuj ją.

3. Uruchom i obserwuj

  1. Uruchom kod w module step1.

Jak widać, jest to prosta aplikacja aparatu. Możesz przełączać się między przednim a tylnym aparatem i dostosowywać proporcje obrazu. Pierwszy przycisk z lewej strony nie robi obecnie nic, ale będzie punktem wejścia do trybu tylnego selfie.

a34aca632d75aa09.png

  1. Teraz spróbuj ustawić urządzenie w położeniu półotwartym, w którym zawias nie jest całkowicie płaski ani zamknięty, ale tworzy kąt 90 stopni.

Jak widać, aplikacja nie reaguje na różne pozycje urządzenia, więc układ się nie zmienia, a zawias pozostaje na środku wizjera.

4. Więcej informacji o bibliotece Jetpack WindowManager

Biblioteka Jetpack WindowManager pomaga programistom tworzyć aplikacje zoptymalizowane pod kątem urządzeń składanych. Zawiera klasę FoldingFeature, która opisuje zagięcie na elastycznym wyświetlaczu lub zawias między dwoma fizycznymi panelami wyświetlacza. Jego interfejs API zapewnia dostęp do ważnych informacji związanych z urządzeniem:

Klasa FoldingFeature zawiera dodatkowe informacje, takie jak occlusionType() czy isSeparating(), ale w tym laboratorium nie będziemy ich szczegółowo omawiać.

Od wersji 1.2.0-beta01 biblioteka używa interfejsu WindowAreaController, który umożliwia przeniesienie bieżącego okna na wyświetlacz wyrównany z tylnym aparatem. Jest to przydatne podczas robienia selfie tylnym aparatem i w wielu innych przypadkach.

Dodawanie zależności

  • Aby używać Jetpack WindowManager w aplikacji, musisz dodać te zależności do pliku build.gradle na poziomie modułu:

step1/build.gradle

def work_version = '1.2.0-beta01'
implementation "androidx.window:window:$work_version"
implementation "androidx.window:window-java:$work_version"
implementation "androidx.window:window-core:$work_version"

Teraz w aplikacji możesz korzystać z klas FoldingFeatureWindowAreaController. Użyj ich, aby stworzyć najlepsze rozwiązanie do obsługi aparatu na urządzeniach składanych.

5. Wdrażanie trybu selfie z tylnego aparatu

Zacznij od trybu tylnego wyświetlacza.

Interfejsem API, który umożliwia ten tryb, jest WindowAreaController. Zapewnia on informacje i działanie związane z przenoszeniem okien między wyświetlaczami lub obszarami wyświetlania na urządzeniu.

Umożliwia wysyłanie zapytań dotyczących listy WindowAreaInfo, z którymi można obecnie wchodzić w interakcje.

Za pomocą WindowAreaInfo możesz uzyskać dostęp do WindowAreaSession, czyli interfejsu reprezentującego aktywny obszar okna i stan dostępności określonego WindowAreaCapability..

  1. Zadeklaruj te zmienne w pliku MainActivity:

step1/MainActivity.kt

private lateinit var windowAreaController: WindowAreaController
private lateinit var displayExecutor: Executor
private var rearDisplaySession: WindowAreaSession? = null
private var rearDisplayWindowAreaInfo: WindowAreaInfo? = null
private var rearDisplayStatus: WindowAreaCapability.Status =
    WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED
private val rearDisplayOperation = WindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA
  1. Zainicjuj je w metodzie onCreate():

step1/MainActivity.kt

displayExecutor = ContextCompat.getMainExecutor(this)
windowAreaController = WindowAreaController.getOrCreate()

lifecycleScope.launch(Dispatchers.Main) {
  lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
    windowAreaController.windowAreaInfos
      .map{info->info.firstOrNull{it.type==WindowAreaInfo.Type.TYPE_REAR_FACING}}
      .onEach { info -> rearDisplayWindowAreaInfo = info }
      .map{it?.getCapability(rearDisplayOperation)?.status?:  WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED }
      .distinctUntilChanged()
      .collect {
           rearDisplayStatus = it
           updateUI()
      }
  }
}
  1. Teraz zaimplementuj funkcję updateUI(), aby włączyć lub wyłączyć przycisk do robienia selfie tylnym aparatem w zależności od bieżącego stanu:

step1/MainActivity.kt

private fun updateUI() {
    if(rearDisplaySession != null) {
        binding.rearDisplay.isEnabled = true
        // A session is already active, clicking on the button will disable it
    } else {
        when(rearDisplayStatus) {
            WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED -> {
                binding.rearDisplay.isEnabled = false
                // RearDisplay Mode is not supported on this device"
            }
            WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNAVAILABLE -> {
                binding.rearDisplay.isEnabled = false
                // RearDisplay Mode is not currently available
            }
            WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE -> {
                binding.rearDisplay.isEnabled = true
                // You can enable RearDisplay Mode
            }
            WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE -> {
                binding.rearDisplay.isEnabled = true
                // You can disable RearDisplay Mode
            }
            else -> {
                binding.rearDisplay.isEnabled = false
                // RearDisplay status is unknown
            }
        }
    }
}

Ten ostatni krok jest opcjonalny, ale warto poznać wszystkie możliwe stany WindowAreaCapability.

  1. Teraz zaimplementuj funkcję toggleRearDisplayMode, która zamknie sesję, jeśli funkcja jest już aktywna, lub wywoła funkcję transferActivityToWindowArea:

step1/CameraViewModel.kt

private fun toggleRearDisplayMode() {
    if(rearDisplayStatus == WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE) {
        if(rearDisplaySession == null) {
            rearDisplaySession = rearDisplayWindowAreaInfo?.getActiveSession(rearDisplayOperation)
        }
        rearDisplaySession?.close()
    } else {
        rearDisplayWindowAreaInfo?.token?.let { token ->
            windowAreaController.transferActivityToWindowArea(
                token = token,
                activity = this,
                executor = displayExecutor,
                windowAreaSessionCallback = this
            )
        }
    }
}

Zwróć uwagę na użycie znaku MainActivity jako WindowAreaSessionCallback.

Interfejs Rear Display API działa na zasadzie detektora: gdy poprosisz o przeniesienie treści na inny wyświetlacz, zainicjujesz sesję, która zostanie zwrócona za pomocą metody onSessionStarted() detektora. Jeśli chcesz wrócić do wewnętrznego (i większego) wyświetlacza, zamknij sesję. Potwierdzenie otrzymasz w onSessionEnded(). Aby utworzyć taki detektor, musisz zaimplementować interfejs WindowAreaSessionCallback.

  1. Zmodyfikuj deklarację MainActivity, aby implementowała interfejs WindowAreaSessionCallback:

step1/MainActivity.kt

class MainActivity : AppCompatActivity(), WindowAreaSessionCallback

Teraz zaimplementuj metody onSessionStartedonSessionEnded w klasie MainActivity. Te metody wywołania zwrotnego są bardzo przydatne do otrzymywania powiadomień o stanie sesji i odpowiedniego aktualizowania aplikacji.

Tym razem jednak dla uproszczenia sprawdź w treści funkcji, czy występują jakieś błędy, i zarejestruj stan.

step1/MainActivity.kt

override fun onSessionEnded(t: Throwable?) {
    if(t != null) {
        Log.d("Something was broken: ${t.message}")
    }else{
        Log.d("rear session ended")
    }
}

override fun onSessionStarted(session: WindowAreaSession) {
    Log.d("rear session started [session=$session]")
}
  1. Skompiluj i uruchom aplikację. Jeśli następnie rozłożysz urządzenie i klikniesz przycisk tylnego wyświetlacza, pojawi się komunikat podobny do tego:

3fa50cce0b0d4b8d.png

  1. Kliknij „Przełącz ekrany”, aby przenieść treści na zewnętrzny wyświetlacz.

6. Wdrażanie trybu na stole

Teraz możesz dostosować aplikację do urządzenia składanego: przenieś treści na bok lub nad zawias urządzenia w zależności od orientacji urządzenia. W tym celu będziesz działać w FoldingStateActor, aby oddzielić kod od Activity i ułatwić jego odczytanie.

Główną częścią tego interfejsu API jest interfejs WindowInfoTracker, który jest tworzony za pomocą metody statycznej wymagającej Activity:

step1/CameraCodelabDependencies.kt

@Provides
fun provideWindowInfoTracker(activity: Activity) =
        WindowInfoTracker.getOrCreate(activity)

Nie musisz pisać tego kodu, ponieważ jest on już obecny, ale warto wiedzieć, jak jest zbudowany element WindowInfoTracker.

  1. Aby monitorować zmiany okna, nasłuchuj tych zmian w metodzie onResume() obiektu Activity:

step1/MainActivity.kt

lifecycleScope.launch {
    foldingStateActor.checkFoldingState(
         this@MainActivity, 
         binding.viewFinder
    )
}
  1. Teraz otwórz plik FoldingStateActor, ponieważ nadszedł czas na wypełnienie metody checkFoldingState().

Jak już wiesz, działa on w fazie RESUMED komponentu Activity i wykorzystuje WindowInfoTracker do monitorowania wszelkich zmian układu.

step1/FoldingStateActor.kt

windowInfoTracker.windowLayoutInfo(activity)
      .collect { newLayoutInfo ->
         activeWindowLayoutInfo = newLayoutInfo
         updateLayoutByFoldingState(cameraViewfinder)
      }

Korzystając z interfejsu WindowInfoTracker, możesz wywołać windowLayoutInfo(), aby zebrać Flow WindowLayoutInfo zawierający wszystkie dostępne informacje w DisplayFeature.

Ostatnim krokiem jest zareagowanie na te zmiany i odpowiednie przeniesienie treści. Robisz to w metodzie updateLayoutByFoldingState(), krok po kroku.

  1. Sprawdź, czy element activityLayoutInfo zawiera właściwości DisplayFeature i czy co najmniej jedna z nich jest typu FoldingFeature. W przeciwnym razie nie wykonuj żadnych działań:

step1/FoldingStateActor.kt

val foldingFeature = activeWindowLayoutInfo?.displayFeatures
            ?.firstOrNull { it is FoldingFeature } as FoldingFeature?
            ?: return
  1. Oblicz położenie zagięcia, aby upewnić się, że położenie urządzenia wpływa na układ i nie wykracza poza granice hierarchii:

step1/FoldingStateActor.kt

val foldPosition = FoldableUtils.getFeaturePositionInViewRect(
            foldingFeature,
            cameraViewfinder.parent as View
        ) ?: return

Teraz masz pewność, że masz FoldingFeature, który wpływa na układ, więc musisz przenieść treści.

  1. Sprawdź, czy FoldingFeature ma wartość HALF_OPEN, w przeciwnym razie przywróć pozycję treści. Jeśli jest to HALF_OPEN, musisz przeprowadzić kolejne sprawdzenie i podjąć inne działania w zależności od orientacji urządzenia po rozłożeniu:

step1/FoldingStateActor.kt

if (foldingFeature.state == FoldingFeature.State.HALF_OPENED) {
    when (foldingFeature.orientation) {
        FoldingFeature.Orientation.VERTICAL -> {
            cameraViewfinder.moveToRightOf(foldPosition)
        }
        FoldingFeature.Orientation.HORIZONTAL -> {
            cameraViewfinder.moveToTopOf(foldPosition)
        }
    }
} else {
    cameraViewfinder.restore()
}

Jeśli zagięcie jest w pozycji VERTICAL, przesuń treści w prawo, w przeciwnym razie przesuń je na pozycję zagięcia.

  1. Zbuduj i uruchom aplikację, a potem otwórz urządzenie i umieść je w trybie stołu, aby zobaczyć, jak treść się przesuwa.

7. Gratulacje!

Z tego laboratorium dowiedziałeś się o niektórych funkcjach, które są dostępne tylko na urządzeniach składanych, takich jak tryb tylnego wyświetlacza czy tryb na stole, oraz o tym, jak je odblokować za pomocą biblioteki Jetpack WindowManager.

Możesz już wdrożyć w aplikacji aparatu funkcje, które zapewnią użytkownikom doskonałe wrażenia.

Więcej informacji

Produkty