Kamera deneyiminizi ortaya çıkarın

1. Başlamadan önce

Katlanabilir cihazları özel kılan nedir?

Katlanabilir telefonlar, nesilde bir kez görülen yeniliklerdir. Benzersiz deneyimler sunmanın yanı sıra eller serbest kullanım için masaüstü kullanıcı arayüzü gibi farklı özelliklerle kullanıcılarınızı memnun edecek benzersiz fırsatlar sunar.

Ön koşullar

  • Android uygulamaları geliştirmeyle ilgili temel bilgiler
  • Hilt Dependency Injection (Hilt Bağımlılığı Enjeksiyonu) çerçevesi hakkında temel bilgiler

Oluşturacaklarınız

Bu codelab'de, katlanabilir cihazlar için optimize edilmiş düzenlerle bir kamera uygulaması geliştireceksiniz.

6caebc2739522a1b.png

Hiçbir cihazın duruşuna tepki vermeyen veya iyileştirilmiş selfie'ler için daha iyi arka kameradan yararlanmayan temel bir kamera uygulamasıyla başlarsınız. Cihaz açık durumdayken önizlemeyi daha küçük ekrana taşımak ve masaüstü modunda ayarlanan telefona tepki vermek için kaynak kodu güncellersiniz.

Kamera uygulaması bu API için en uygun kullanım alanı olsa da bu codelab'de öğrendiğiniz her iki özellik de herhangi bir uygulamaya uygulanabilir.

Neler öğreneceksiniz?

  • Duruş değişimine tepki vermek için Jetpack Window Manager'ı kullanma
  • Uygulamanızı katlanabilir ekranın daha küçük ekranına taşıma

Gerekenler

  • Android Studio'nun son sürümü
  • Katlanabilir cihaz veya katlanabilir emülatör

2. Hazırlanın

Başlangıç kodunu alın

  1. Git yüklüyse aşağıdaki komutu çalıştırabilirsiniz. Git'in yüklü olup olmadığını kontrol etmek için terminale veya komut satırına git --version yazın ve doğru şekilde çalıştığını doğrulayın.
git clone https://github.com/android/large-screen-codelabs.git
  1. İsteğe bağlı: Git'iniz yoksa bu codelab'e ait tüm kodları indirmek için şu düğmeyi tıklayabilirsiniz:

İlk modülü açın

  • Android Studio'da /step1 altındaki ilk modülü açın.

Bu codelab ile ilgili kodu gösteren Android Studio ekran görüntüsü

En son Gradle sürümünü kullanmanız istenirse gidip güncelleyin.

3. Çalıştır ve gözlemle

  1. step1 modülünde kodu çalıştırın.

Gördüğünüz gibi bu basit bir kamera uygulaması. Ön ve arka kamera arasında geçiş yapabilir ve en boy oranını ayarlayabilirsiniz. Bununla birlikte, soldan ilk düğme şu anda herhangi bir işlem yapmıyor ancak bu düğme, Arka Selfie modunun giriş noktası olacak.

a34aca632d75aa09.png

  1. Şimdi cihazı, menteşenin tamamen düz veya kapalı olmadığı ancak 90 derecelik açı oluşturduğu yarı açık bir pozisyona yerleştirmeye çalışın.

Gördüğünüz gibi uygulama farklı cihaz duruşlarına yanıt vermiyor. Dolayısıyla düzen değişmiyor, menteşeyi vizörün ortasında bırakıyorsunuz.

4. Jetpack WindowManager hakkında bilgi

Jetpack WindowManager kitaplığı, uygulama geliştiricilerin katlanabilir cihazlar için optimize edilmiş deneyimler oluşturmalarına yardımcı olur. Esnek ekranda kıvrımı veya iki fiziksel ekran paneli arasındaki menteşeyi tanımlayan FoldingFeature sınıfını içerir. API'si cihazla ilgili önemli bilgilere erişim sağlar:

FoldingFeature sınıfı occlusionType() veya isSeparating() gibi ek bilgiler içerir ancak bu codelabe'de bu bilgiler ayrıntılı olarak ele alınmamıştır.

Kitaplık, 1.2.0-beta01 sürümünden itibaren WindowAreaController adlı API'yi kullanır. Bu API, mevcut pencereyi arka kamerayla aynı hizada ekrana taşımayı sağlayan bir API'dir. Bu API, arka kamerayla ve daha birçok kullanım alanında selfie çekmek için idealdir.

Bağımlılık ekleme

  • Uygulamanızda Jetpack WindowManager'ı kullanmak için, modül düzeyindeki build.gradle dosyanıza şu bağımlılıkları eklemeniz gerekir:

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"

Artık uygulamanızda hem FoldingFeature hem de WindowAreaController sınıflarına erişebilirsiniz. En iyi katlanabilir kamera deneyimini oluşturmak için bu cihazları kullanıyorsunuz.

5. Arka Selfie modunu uygulama

Arka Ekran modu ile başlayın.

WindowAreaController, bu moda izin veren API'dir. Bu API, pencereleri bir cihazdaki ekranlar veya görüntüleme alanları arasında taşımayla ilgili bilgiler ve davranışlar sağlar.

Şu anda etkileşimde bulunabileceğiniz WindowAreaInfo listesini sorgulamanıza olanak tanır.

WindowAreaInfo kullanarak, etkin pencere alanı özelliğini temsil eden WindowAreaSession adlı arayüze ve belirli bir WindowAreaCapability. için kullanılabilirlik durumuna erişebilirsiniz.

  1. Şu değişkenleri MainActivity içinde tanımlayın:

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. Ardından bunları onCreate() yönteminde başlatın:

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. Şimdi, mevcut duruma bağlı olarak arka selfie düğmesini etkinleştirmek veya devre dışı bırakmak için updateUI() işlevini uygulayın:

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
            }
        }
    }
}

Bu son adım isteğe bağlıdır ancak WindowAreaCapability. öğesinin olası tüm durumlarını öğrenmek

  1. Şimdi, özellik zaten etkinse oturumu kapatacak toggleRearDisplayMode işlevini uygulayın veya transferActivityToWindowArea işlevini çağırın:

step1/KameraViewModel.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
            )
        }
    }
}

MainActivity öğesinin, WindowAreaSessionCallback olarak kullanıldığına dikkat edin.

Rear Display API, dinleyici yaklaşımıyla çalışır: İçeriği diğer ekrana taşıma isteğinde bulunduğunuzda dinleyicinin onSessionStarted() yöntemiyle döndürülen bir oturum başlatırsınız. Bunun yerine iç (ve daha büyük) ekrana dönmek istediğinizde oturumu kapatır ve onSessionEnded() yönteminde bir onay alırsınız. Böyle bir işleyici oluşturmak için WindowAreaSessionCallback arayüzünü uygulamanız gerekir.

  1. MainActivity bildirimini, WindowAreaSessionCallback arayüzünü uygulayacak şekilde değiştirin:

step1/MainActivity.kt

class MainActivity : AppCompatActivity(), WindowAreaSessionCallback

Şimdi, MainActivity içindeki onSessionStarted ve onSessionEnded yöntemlerini uygulayın. Bu geri çağırma yöntemleri, oturum durumu hakkında bildirim almak ve uygulamayı buna göre güncellemek açısından son derece yararlıdır.

Ancak bu kez, kolaylık sağlaması için işlev gövdesinde herhangi bir hata olup olmadığını kontrol edin ve durumu günlüğe kaydedin.

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. Uygulamayı derleyip çalıştırın. Ardından, cihazınızı açıp arka ekran düğmesine dokunursanız şuna benzer bir mesaj görüntülenir:

3fa50cce0b0d4b8d.png

  1. İçeriğinizin dış ekrana taşınması için "Şimdi ekranları değiştir"i seçin.

6. Masa Üstü modunu uygulama

Artık uygulamanızı katlanabilir hale getirmenin zamanı geldi: İçeriğinizi, ekranın yönüne bağlı olarak cihazın yan veya menteşesinin üstüne veya yan tarafına doğru hareket ettirebilirsiniz. Bunu yapmak için FoldingStateActor içinde hareket edersiniz. Böylece, kodunuzun daha kolay okunması için Activity ile ayrılırsınız.

Bu API'nin temel bölümü, Activity gerektiren statik bir yöntemle oluşturulan WindowInfoTracker arayüzünden oluşur:

step1/KameraCodelabDependencies.kt

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

Bu kodu zaten mevcut olduğu için yazmanız gerekmez ancak WindowInfoTracker'nın nasıl oluşturulduğunu anlamak yararlıdır.

  1. Pencere değişikliklerini dinlemek için Activity cihazınızın onResume() yönteminde şu değişiklikleri dinleyin:

step1/MainActivity.kt

lifecycleScope.launch {
    foldingStateActor.checkFoldingState(
         this@MainActivity, 
         binding.viewFinder
    )
}
  1. checkFoldingState() yöntemini doldurma zamanı geldiğinden şimdi FoldingStateActor dosyasını açın.

Daha önce de gördüğünüz gibi, Activity uygulamanızın RESUMED aşamasında çalışır ve düzen değişikliklerini dinlemek için WindowInfoTracker özelliğinden yararlanır.

step1/FoldingStateActor.kt

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

WindowInfoTracker arayüzünü kullanarak windowLayoutInfo() numaralı telefonu arayarak DisplayFeature sayfasındaki mevcut tüm bilgileri içeren WindowLayoutInfo Flow yapabilirsiniz.

Son adım, bu değişikliklere tepki vermek ve içeriği buna uygun şekilde taşımaktır. Bu işlemi updateLayoutByFoldingState() yönteminin içinde her defasında bir adımla gerçekleştirirsiniz.

  1. activityLayoutInfo öğesinin bazı DisplayFeature özelliklerini içerdiğinden ve bu özelliklerden en az birinin FoldingFeature olduğundan emin olun. Aksi takdirde herhangi bir işlem yapmak istemezsiniz:

step1/FoldingStateActor.kt

val foldingFeature = activeWindowLayoutInfo?.displayFeatures
            ?.firstOrNull { it is FoldingFeature } as FoldingFeature?
            ?: return
  1. Cihaz konumunun düzeninizi etkilediğinden ve hiyerarşinizin sınırlarının dışında olmadığından emin olmak için ekranın konumunu hesaplayın:

step1/FoldingStateActor.kt

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

Düzeninizi etkileyen bir FoldingFeature olduğundan emin olduğunuz için içeriğinizi taşımanız gerekiyor.

  1. FoldingFeature öğesinin HALF_OPEN olup olmadığını kontrol edin, yoksa yalnızca içeriğinizin konumunu geri yükleyin. HALF_OPEN ise başka bir kontrol gerçekleştirmeniz ve ekranın yönüne bağlı olarak farklı hareket etmeniz gerekir:

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()
}

Ekranın üst kısmı VERTICAL ise içeriğinizi sağa, aksi takdirde ekranın üst konumuna taşırsınız.

  1. Uygulamanızı derleyip çalıştırın, ardından cihazınızı açın ve içeriğin uygun şekilde hareket ettiğini görmek için masaüstü moduna yerleştirin.

7. Tebrikler!

Bu codelab'de, Arka Ekran Modu veya Masa Üstü modu gibi katlanabilir cihazlara özgü bazı özellikler ve bunların kilidini Jetpack WindowManager kullanarak nasıl açabileceğiniz hakkında bilgi edindiniz.

Kamera uygulamanız için mükemmel kullanıcı deneyimleri uygulamaya hazırsınız.

Daha fazla bilgi

Reference