Skip to content

Commit

Permalink
Added integration for Photo Editor app with new screen manager SDK (#48)
Browse files Browse the repository at this point in the history
Added integration for Photo Editor app with new screen manager SDK
  • Loading branch information
soalb-m authored Dec 9, 2020
1 parent 6c24aaa commit c7f949c
Show file tree
Hide file tree
Showing 37 changed files with 436 additions and 206 deletions.
7 changes: 4 additions & 3 deletions PhotoEditor/app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/

apply plugin: 'com.android.application'
Expand Down Expand Up @@ -33,14 +32,16 @@ android {
}

dependencies {
implementation microsoftDependencies.screenManager
implementation microsoftDependencies.layouts
implementation microsoftDependencies.fragmentsHandler

implementation kotlinDependencies.kotlinStdlib
implementation androidxDependencies.appCompat
implementation androidxDependencies.constraintLayout
implementation androidxDependencies.ktxCore
implementation androidxDependencies.ktxFragment

implementation microsoftDependencies.dualScreenLayout

testImplementation testDependencies.junit
androidTestImplementation instrumentationTestDependencies.junit
androidTestImplementation instrumentationTestDependencies.espressoCore
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

package com.microsoft.device.display.samples.photoeditor

import android.content.Intent
Expand All @@ -18,8 +23,12 @@ import androidx.test.rule.ActivityTestRule
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.microsoft.device.dualscreen.layout.ScreenHelper
import com.microsoft.device.display.samples.photoeditor.utils.ScreenInfoListenerImpl
import com.microsoft.device.dualscreen.ScreenManagerProvider
import org.hamcrest.CoreMatchers.not
import org.junit.After
import org.junit.Assert
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
Expand All @@ -34,6 +43,21 @@ import org.hamcrest.CoreMatchers.`is` as iz
class PhotoEditorUITest {
@get:Rule
val activityRule = ActivityTestRule(MainActivity::class.java)
private var screenInfoListener = ScreenInfoListenerImpl()

@Before
fun setup() {
val screenManager = ScreenManagerProvider.getScreenManager()
screenManager.addScreenInfoListener(screenInfoListener)
}

@After
fun tearDown() {
val screenManager = ScreenManagerProvider.getScreenManager()
screenManager.removeScreenInfoListener(screenInfoListener)
screenInfoListener.resetScreenInfo()
screenInfoListener.resetScreenInfoCounter()
}

/**
* Tests visibility of controls when app spanned vs. unspanned
Expand All @@ -53,7 +77,7 @@ class PhotoEditorUITest {
onView(withId(R.id.warmth)).check(matches(withEffectiveVisibility(Visibility.INVISIBLE)))

spanFromLeft()
assertThat(isSpanned(), iz(true))
waitForScreenInfoAndAssert { Assert.assertTrue(isSpanned()) }

// Switched to dual-screen mode, so dropdown should not exist and all sliders should be visible
onView(withId(R.id.controls)).check(doesNotExist())
Expand Down Expand Up @@ -100,7 +124,10 @@ class PhotoEditorUITest {
device.wait(Until.hasObject(By.pkg(filesPackage).depth(0)), 3000) // timeout at 3 seconds

// Before import, drawable is equal to prev
assertThat(prev, iz(activityRule.activity.findViewById<ImageFilterView>(R.id.image).drawable))
assertThat(
prev,
iz(activityRule.activity.findViewById<ImageFilterView>(R.id.image).drawable)
)

// Hardcoded to select most recently saved file in Files app - must be an image file
device.swipe(1550, 1230, 1550, 1230, 100)
Expand Down Expand Up @@ -207,8 +234,13 @@ class PhotoEditorUITest {
device.swipe(rightX, bottomY, rightX, middleY, closeSteps)
}

private fun waitForScreenInfoAndAssert(assert: () -> Unit) {
screenInfoListener.waitForScreenInfoChanges()
assert()
screenInfoListener.resetScreenInfo()
}

private fun isSpanned(): Boolean {
onIdle() // wait until layout changes have been fully processed before checking
return ScreenHelper.isDualMode(activityRule.activity)
return screenInfoListener.screenInfo?.isDualMode() == true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

package com.microsoft.device.display.samples.photoeditor.utils

import com.microsoft.device.dualscreen.ScreenInfo
import com.microsoft.device.dualscreen.ScreenInfoListener
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

/**
* Simple implementation for [ScreenInfoListener] that saves internally the last screen info data.
*/
class ScreenInfoListenerImpl : ScreenInfoListener {
private var _screenInfo: ScreenInfo? = null
val screenInfo: ScreenInfo?
get() = _screenInfo
private var screenInfoLatch: CountDownLatch? = null

override fun onScreenInfoChanged(screenInfo: ScreenInfo) {
_screenInfo = screenInfo
screenInfoLatch?.countDown()
}

/**
* Resets the last screen info to [null]
*/
fun resetScreenInfo() {
_screenInfo = null
}

/**
* Resets screen info counter when waiting for a screen changes to happen before calling
* [waitForScreenInfoChanges].
*/
fun resetScreenInfoCounter() {
screenInfoLatch = CountDownLatch(1)
}

/**
* Blocks and waits for the next screen info changes to happen.
* @return {@code true} if the screen info changed before the timeout count reached zero and
* {@code false} if the waiting time elapsed before the changes happened.
*/
fun waitForScreenInfoChanges(): Boolean {
return try {
val result = screenInfoLatch?.await(10, TimeUnit.SECONDS) ?: false
result
} catch (e: InterruptedException) {
false
}
}
}
2 changes: 1 addition & 1 deletion PhotoEditor/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) Microsoft Corporation. All rights reserved.
~ Licensed under the MIT License.
~
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.microsoft.device.display.samples.photoeditor">

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:name=".PhotoEditorApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

package com.microsoft.device.display.samples.photoeditor

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer

fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
class RemovableObserver : Observer<T> {
override fun onChanged(t: T?) {
observer.onChanged(t)
removeObserver(this)
}
}

observe(lifecycleOwner, RemovableObserver())
}
Loading

0 comments on commit c7f949c

Please sign in to comment.