Merge "Consolidate PiP demos in ApiDemos app"

This commit is contained in:
Hongwei Wang
2021-12-22 04:15:43 +00:00
committed by Android (Google) Code Review
11 changed files with 340 additions and 573 deletions

View File

@@ -290,45 +290,9 @@
android:label="@string/activity_picture_in_picture"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<activity android:name=".app.PictureInPictureAutoEnter"
android:label="@string/activity_picture_in_picture_auto_enter"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:theme="@style/Theme.NoActionBar"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<activity android:name=".app.PictureInPictureSeamlessResize"
android:label="@string/activity_picture_in_picture_seamless_resize"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<activity android:name=".app.PictureInPictureSourceRectHint"
android:label="@string/activity_picture_in_picture_source_rect_hint"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation">
"screenSize|smallestScreenSize|screenLayout|orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />

View File

@@ -15,18 +15,106 @@
~ limitations under the License.
-->
<!-- Demonstrates implementation of picture-in-picture. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="4dp"
android:gravity="center_horizontal"
<!--
Demonstrates Picture-In-Picture with various configurations.
- Enter PiP with on-screen button
- Enter PiP by swiping up to home or tap on home button
- Toggle the auto enter PiP flag on and off
- Toggle the source rect hint on and off
- Toggle the seamless resize flag on and off
- Change the position of current and next source rect hint
- Tablet layout on foldables
- Enter content PiP
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:orientation="vertical">
<Button android:layout_width="wrap_content"
<!-- layout params would be changed programmatically -->
<com.example.android.apis.view.FixedAspectRatioImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/enter_pip"
android:text="@string/enter_picture_in_picture">
</Button>
</LinearLayout>
android:scaleType="fitXY"
android:src="@drawable/sample_1"
app:aspectRatio="16/9" />
<ScrollView
android:id="@+id/control_group"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Switch
android:id="@+id/auto_pip_toggle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/activity_picture_in_picture_auto_pip_toggle" />
<Switch
android:id="@+id/source_rect_hint_toggle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/activity_picture_in_picture_source_rect_hint_toggle" />
<Switch
android:id="@+id/seamless_resize_toggle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/activity_picture_in_picture_seamless_resize_toggle" />
<RadioGroup
android:id="@+id/current_position"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_current_position" />
<RadioButton
android:id="@+id/radio_current_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_position_start" />
<RadioButton
android:id="@+id/radio_current_end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_position_end" />
</RadioGroup>
<Button
android:id="@+id/enter_pip_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/enter_picture_in_picture" />
<Button
android:id="@+id/enter_content_pip_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/enter_content_pip" />
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<!-- Demonstrates Picture-In-Picture with auto enter enabled. -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- layout params would be changed programmatically -->
<com.example.android.apis.view.FixedAspectRatioImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@drawable/sample_1"
app:aspectRatio="16/9" />
<Switch android:id="@+id/source_rect_hint_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:padding="8dp"
android:text="@string/activity_picture_in_picture_source_rect_hint_toggle" />
<Button android:id="@+id/change_orientation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:padding="8dp"
android:text="@string/activity_picture_in_picture_change_orientation" />
</LinearLayout>

View File

@@ -1,47 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<!-- Demonstrates Picture-In-Picture's Seamless Resize. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/activity_picture_in_picture_seamless_resize_demo_text"
android:textSize="18sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<Switch
android:id="@+id/seamless_resize_switch"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/activity_picture_in_picture_seamless_resize_switch"
android:checked="false"
android:background="@android:color/white"
android:padding="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,56 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<!-- Demonstrates Picture-In-Picture's Source Rect Hint usage. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/frame_layout"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/entry_source_btn">
<VideoView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"/>
</FrameLayout>
<Button
android:id="@+id/entry_source_btn"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/exit_source_btn"/>
<Button
android:id="@+id/exit_source_btn"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -63,21 +63,19 @@
<string name="screen_orientation">Screen Orientation</string>
<string name="activity_picture_in_picture">App/Activity/Picture in Picture</string>
<string name="activity_picture_in_picture_auto_enter">App/Activity/Picture in Picture Auto Enter</string>
<string name="activity_picture_in_picture_source_rect_hint_toggle">Toggle source rect hint</string>
<string name="activity_picture_in_picture_seamless_resize">App/Activity/Picture in Picture Seamless Resize</string>
<string name="activity_picture_in_picture_source_rect_hint">App/Activity/Picture in Picture Source Rect Hint</string>
<string name="activity_picture_in_picture_source_rect_hint_current_position">Current position: %1$s</string>
<string name="activity_picture_in_picture_source_rect_hint_position_on_exit">Position on exit: %1$s</string>
<string name="activity_picture_in_picture_change_orientation">Change orientation</string>
<string name="activity_picture_in_picture_seamless_resize_switch">Turn on Seamless Resize (recommend off)</string>
<string name="activity_picture_in_picture_seamless_resize_demo_text">
The quick brown fox jumps over the lazy dog.
</string>
<string name="activity_picture_in_picture_auto_pip_toggle">Enable auto PiP</string>
<string name="activity_picture_in_picture_source_rect_hint_toggle">Enable source rect hint</string>
<string name="activity_picture_in_picture_seamless_resize_toggle">Enable seamless resize</string>
<string name="enter_content_pip">Enter content PiP</string>
<string name="enter_picture_in_picture">Manually enter PiP</string>
<string name="label_current_position">Current position</string>
<string name="label_exit_position">Exit position</string>
<string name="label_position_start">Start</string>
<string name="label_position_end">End</string>
<string name="activity_max_aspect_ratio_square">App/Activity/Max Aspect Ratio/1:1</string>
<string name="activity_max_aspect_ratio_16to9">App/Activity/Max Aspect Ratio/16:9</string>
<string name="activity_max_aspect_ratio_any">App/Activity/Max Aspect Ratio/Any</string>
<string name="enter_picture_in_picture">Enter picture-in-picture mode</string>
<string name="activity_translucent">App/Activity/Translucent</string>
<string name="translucent_background">Example of how you can make an

View File

@@ -17,26 +17,251 @@
package com.example.android.apis.app;
import android.app.Activity;
import android.app.PictureInPictureParams;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.widget.Button;
import android.util.Rational;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.Switch;
import com.example.android.apis.R;
public class PictureInPicture extends Activity {
private static final String EXTRA_ENABLE_AUTO_PIP = "auto_pip";
private static final String EXTRA_ENABLE_SOURCE_RECT_HINT = "source_rect_hint";
private static final String EXTRA_ENABLE_SEAMLESS_RESIZE = "seamless_resize";
private static final String EXTRA_CURRENT_POSITION = "current_position";
private Button mEnterPip;
private final View.OnLayoutChangeListener mOnLayoutChangeListener =
(v, oldLeft, oldTop, oldRight, oldBottom, newLeft, newTop, newRight, newBottom) -> {
updatePictureInPictureParams();
};
private final CompoundButton.OnCheckedChangeListener mOnToggleChangedListener =
(v, isChecked) -> updatePictureInPictureParams();
private final RadioGroup.OnCheckedChangeListener mOnPositionChangedListener =
(v, id) -> updateContentPosition(id);
private LinearLayout mContainer;
private View mImageView;
private View mControlGroup;
private Switch mAutoPipToggle;
private Switch mSourceRectHintToggle;
private Switch mSeamlessResizeToggle;
private RadioGroup mCurrentPositionGroup;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.picture_in_picture);
mEnterPip = (Button)findViewById(R.id.enter_pip);
mEnterPip.setOnClickListener((v) -> enterPictureInPictureMode());
// Find views
mContainer = findViewById(R.id.container);
mImageView = findViewById(R.id.image);
mControlGroup = findViewById(R.id.control_group);
mAutoPipToggle = findViewById(R.id.auto_pip_toggle);
mSourceRectHintToggle = findViewById(R.id.source_rect_hint_toggle);
mSeamlessResizeToggle = findViewById(R.id.seamless_resize_toggle);
mCurrentPositionGroup = findViewById(R.id.current_position);
// Attach listeners
mImageView.addOnLayoutChangeListener(mOnLayoutChangeListener);
mAutoPipToggle.setOnCheckedChangeListener(mOnToggleChangedListener);
mSourceRectHintToggle.setOnCheckedChangeListener(mOnToggleChangedListener);
mSeamlessResizeToggle.setOnCheckedChangeListener(mOnToggleChangedListener);
mCurrentPositionGroup.setOnCheckedChangeListener(mOnPositionChangedListener);
findViewById(R.id.enter_pip_button).setOnClickListener(v -> enterPictureInPictureMode());
findViewById(R.id.enter_content_pip_button).setOnClickListener(v -> enterContentPip());
// Set defaults
final Intent intent = getIntent();
mAutoPipToggle.setChecked(intent.getBooleanExtra(EXTRA_ENABLE_AUTO_PIP, false));
mSourceRectHintToggle.setChecked(
intent.getBooleanExtra(EXTRA_ENABLE_SOURCE_RECT_HINT, false));
mSeamlessResizeToggle.setChecked(
intent.getBooleanExtra(EXTRA_ENABLE_SEAMLESS_RESIZE, false));
final int positionId = "end".equalsIgnoreCase(
intent.getStringExtra(EXTRA_CURRENT_POSITION))
? R.id.radio_current_end
: R.id.radio_current_start;
mCurrentPositionGroup.check(positionId);
updateLayout(getResources().getConfiguration());
}
@Override
protected void onUserLeaveHint() {
enterPictureInPictureMode();
// Only used when auto PiP is disabled. This is to simulate the behavior that an app
// supports regular PiP but not auto PiP.
if (!mAutoPipToggle.isChecked()) {
enterPictureInPictureMode();
}
}
@Override
public void onConfigurationChanged(Configuration newConfiguration) {
super.onConfigurationChanged(newConfiguration);
updateLayout(newConfiguration);
}
@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode,
Configuration newConfig) {
if (!isInPictureInPictureMode) {
// When it's about to exit PiP mode, always reset the mImageView position to start.
// If position is previously set to end, this should demonstrate the exit
// source rect hint behavior introduced in S.
mCurrentPositionGroup.check(R.id.radio_current_start);
}
}
private void enterContentPip() {
// TBD
}
private void updateLayout(Configuration configuration) {
mImageView.addOnLayoutChangeListener(mOnLayoutChangeListener);
final boolean isTablet = configuration.screenWidthDp >= 800;
final boolean isLandscape =
(configuration.orientation == Configuration.ORIENTATION_LANDSCAPE);
final boolean isPictureInPicture = isInPictureInPictureMode();
if (isPictureInPicture) {
setupPictureInPictureLayout();
} else if (isTablet && isLandscape) {
setupTabletLandscapeLayout();
} else if (isLandscape) {
setupFullScreenLayout();
} else {
setupRegularLayout();
}
}
private void setupPictureInPictureLayout() {
mControlGroup.setVisibility(View.GONE);
final LinearLayout.LayoutParams imageLp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
imageLp.gravity = Gravity.NO_GRAVITY;
mImageView.setLayoutParams(imageLp);
}
private void setupTabletLandscapeLayout() {
mControlGroup.setVisibility(View.VISIBLE);
exitFullScreenMode();
final LinearLayout.LayoutParams imageLp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
imageLp.gravity = Gravity.NO_GRAVITY;
enterTwoPaneMode(imageLp);
}
private void setupFullScreenLayout() {
mControlGroup.setVisibility(View.GONE);
enterFullScreenMode();
final LinearLayout.LayoutParams imageLp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.MATCH_PARENT);
imageLp.gravity = Gravity.CENTER_HORIZONTAL;
enterOnePaneMode(imageLp);
}
private void setupRegularLayout() {
mControlGroup.setVisibility(View.VISIBLE);
exitFullScreenMode();
final LinearLayout.LayoutParams imageLp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
imageLp.gravity = Gravity.NO_GRAVITY;
enterOnePaneMode(imageLp);
}
private void enterOnePaneMode(LinearLayout.LayoutParams imageLp) {
mContainer.setOrientation(LinearLayout.VERTICAL);
final LinearLayout.LayoutParams controlLp =
(LinearLayout.LayoutParams) mControlGroup.getLayoutParams();
controlLp.width = LinearLayout.LayoutParams.MATCH_PARENT;
controlLp.height = 0;
controlLp.weight = 1;
mControlGroup.setLayoutParams(controlLp);
imageLp.weight = 0;
mImageView.setLayoutParams(imageLp);
}
private void enterTwoPaneMode(LinearLayout.LayoutParams imageLp) {
mContainer.setOrientation(LinearLayout.HORIZONTAL);
final LinearLayout.LayoutParams controlLp =
(LinearLayout.LayoutParams) mControlGroup.getLayoutParams();
controlLp.width = 0;
controlLp.height = LinearLayout.LayoutParams.MATCH_PARENT;
controlLp.weight = 1;
mControlGroup.setLayoutParams(controlLp);
imageLp.width = 0;
imageLp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
imageLp.weight = 1;
mImageView.setLayoutParams(imageLp);
}
private void enterFullScreenMode() {
// TODO(b/188001699) switch to use insets controller once the bug is fixed.
final View decorView = getWindow().getDecorView();
final int systemUiNavigationBarFlags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility()
| systemUiNavigationBarFlags);
}
private void exitFullScreenMode() {
final View decorView = getWindow().getDecorView();
final int systemUiNavigationBarFlags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility()
& ~systemUiNavigationBarFlags);
}
private void updatePictureInPictureParams() {
mImageView.removeOnLayoutChangeListener(mOnLayoutChangeListener);
// do not bother PictureInPictureParams update when it's already in pip mode.
if (isInPictureInPictureMode()) return;
final Rect imageViewRect = new Rect();
mImageView.getGlobalVisibleRect(imageViewRect);
// bail early if mImageView has not been measured yet
if (imageViewRect.isEmpty()) return;
final PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder()
.setAutoEnterEnabled(mAutoPipToggle.isChecked())
.setSourceRectHint(mSourceRectHintToggle.isChecked()
? new Rect(imageViewRect) : null)
.setSeamlessResizeEnabled(mSeamlessResizeToggle.isChecked())
.setAspectRatio(new Rational(imageViewRect.width(), imageViewRect.height()));
setPictureInPictureParams(builder.build());
}
private void updateContentPosition(int checkedId) {
mContainer.removeAllViews();
mImageView.addOnLayoutChangeListener(mOnLayoutChangeListener);
if (checkedId == R.id.radio_current_start) {
mContainer.addView(mImageView, 0);
mContainer.addView(mControlGroup, 1);
} else {
mContainer.addView(mControlGroup, 0);
mContainer.addView(mImageView, 1);
}
}
}

View File

@@ -1,138 +0,0 @@
/*
* Copyright (C) 2021 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.example.android.apis.app;
import android.app.Activity;
import android.app.PictureInPictureParams;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Rational;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.Switch;
import com.example.android.apis.R;
public class PictureInPictureAutoEnter extends Activity {
private final View.OnLayoutChangeListener mOnLayoutChangeListener =
(v, oldLeft, oldTop, oldRight, oldBottom, newLeft, newTop, newRight, newBottom) -> {
updatePictureInPictureParams();
};
private final CompoundButton.OnCheckedChangeListener mOnCheckedChangeListener =
(v, isChecked) -> updatePictureInPictureParams();
private View mImageView;
private View mButtonView;
private Switch mSwitchView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.picture_in_picture_auto_enter);
mImageView = findViewById(R.id.image);
mImageView.addOnLayoutChangeListener(mOnLayoutChangeListener);
mButtonView = findViewById(R.id.change_orientation);
mButtonView.setOnClickListener((v) -> {
final int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
});
mSwitchView = findViewById(R.id.source_rect_hint_toggle);
mSwitchView.setOnCheckedChangeListener(mOnCheckedChangeListener);
// there is a bug that setSourceRectHint(null) does not clear the source rect hint
// once there is a non-null source rect hint ever been set. set this to false by default
// therefore this demo activity can be used for testing autoEnterPip behavior without
// source rect hint when launched for the first time.
mSwitchView.setChecked(false);
updateLayout(getResources().getConfiguration());
}
@Override
public void onConfigurationChanged(Configuration newConfiguration) {
super.onConfigurationChanged(newConfiguration);
updateLayout(newConfiguration);
}
private void updateLayout(Configuration configuration) {
final boolean isLandscape =
(configuration.orientation == Configuration.ORIENTATION_LANDSCAPE);
final boolean isPictureInPicture = isInPictureInPictureMode();
mButtonView.setVisibility((isPictureInPicture || isLandscape) ? View.GONE : View.VISIBLE);
mSwitchView.setVisibility((isPictureInPicture || isLandscape) ? View.GONE: View.VISIBLE);
final LinearLayout.LayoutParams layoutParams;
if (isPictureInPicture) {
layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
layoutParams.gravity = Gravity.NO_GRAVITY;
} else {
// Toggle the fullscreen mode as well.
// TODO(b/188001699) switch to use insets controller once the bug is fixed.
final View decorView = getWindow().getDecorView();
final int systemUiNavigationBarFlags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
if (isLandscape) {
layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.MATCH_PARENT);
layoutParams.gravity = Gravity.CENTER_HORIZONTAL;
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility()
| systemUiNavigationBarFlags);
} else {
layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.NO_GRAVITY;
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility()
& ~systemUiNavigationBarFlags);
}
}
mImageView.addOnLayoutChangeListener(mOnLayoutChangeListener);
mImageView.setLayoutParams(layoutParams);
}
private void updatePictureInPictureParams() {
mImageView.removeOnLayoutChangeListener(mOnLayoutChangeListener);
// do not bother PictureInPictureParams update when it's already in pip mode.
if (isInPictureInPictureMode()) return;
final Rect imageViewRect = new Rect();
mImageView.getGlobalVisibleRect(imageViewRect);
// bail early if mImageView has not been measured yet
if (imageViewRect.isEmpty()) return;
final Rect sourceRectHint = mSwitchView.isChecked() ? new Rect(imageViewRect) : null;
final PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder()
.setAutoEnterEnabled(true)
.setAspectRatio(new Rational(imageViewRect.width(), imageViewRect.height()))
.setSourceRectHint(sourceRectHint);
setPictureInPictureParams(builder.build());
}
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) 2021 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.example.android.apis.app;
import android.app.Activity;
import android.app.PictureInPictureParams;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.View;
import android.widget.Switch;
import com.example.android.apis.R;
public class PictureInPictureSeamlessResize extends Activity {
private Switch mSwitchView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().hide();
setContentView(R.layout.picture_in_picture_seamless_resize);
mSwitchView = findViewById(R.id.seamless_resize_switch);
mSwitchView.setOnCheckedChangeListener((v, isChecked) -> {
onSeamlessResizeCheckedChanged(isChecked);
});
onSeamlessResizeCheckedChanged(mSwitchView.isChecked());
}
@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode,
Configuration newConfig) {
mSwitchView.setVisibility(isInPictureInPictureMode ? View.GONE : View.VISIBLE);
}
@Override
public boolean onPictureInPictureRequested() {
enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
return true;
}
private void onSeamlessResizeCheckedChanged(boolean checked) {
final PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder()
.setSeamlessResizeEnabled(checked);
setPictureInPictureParams(builder.build());
}
}

View File

@@ -1,156 +0,0 @@
/*
* Copyright (C) 2021 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.example.android.apis.app;
import android.app.Activity;
import android.app.PictureInPictureParams;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.util.Rational;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.VideoView;
import com.example.android.apis.R;
public class PictureInPictureSourceRectHint extends Activity {
private FrameLayout mFrameLayout;
private VideoView mVideoView;
private Button mEntryButton;
private Button mExitButton;
private int mPositionOnEntry;
private int mPositionOnExit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().hide();
setContentView(R.layout.picture_in_picture_source_rect_hint);
mFrameLayout = findViewById(R.id.frame_layout);
mVideoView = findViewById(R.id.video_view);
mEntryButton = findViewById(R.id.entry_source_btn);
mExitButton = findViewById(R.id.exit_source_btn);
initPlayer(Uri.parse("android.resource://" + getPackageName() +
"/" + R.raw.videoviewdemo));
setEntryState(Gravity.TOP);
setExitState(Gravity.TOP);
mEntryButton.setOnClickListener(v -> {
// Toggle the position and update the views.
setEntryState(mPositionOnEntry == Gravity.TOP ? Gravity.BOTTOM : Gravity.TOP);
});
mExitButton.setOnClickListener(v -> {
// Toggle the position and update the views.
setExitState(mPositionOnExit == Gravity.TOP ? Gravity.BOTTOM : Gravity.TOP);
});
mVideoView.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
if (left == oldLeft && right == oldRight && top == oldTop
&& bottom == oldBottom) return;
setPictureInPictureParams(new PictureInPictureParams.Builder()
.setSourceRectHint(getVideoRectHint())
.build());
});
}
@Override
protected void onDestroy() {
super.onDestroy();
mVideoView.stopPlayback();
}
@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode,
Configuration newConfig) {
mEntryButton.setVisibility(isInPictureInPictureMode ? View.GONE : View.VISIBLE);
mExitButton.setVisibility(isInPictureInPictureMode ? View.GONE : View.VISIBLE);
final FrameLayout.LayoutParams params;
if (isInPictureInPictureMode) {
// In PIP mode the video should take up the full width and height.
params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
} else {
// Exiting PIP, return the video to its original size and place it to the preferred
// gravity selected before entering PIP mode.
params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
params.gravity = mPositionOnExit;
// The "exit" gravity becomes the current "entry" gravity, update it and its views.
setEntryState(mPositionOnExit);
}
mVideoView.setLayoutParams(params);
}
@Override
public boolean onPictureInPictureRequested() {
final Rect hint = getVideoRectHint();
enterPictureInPictureMode(new PictureInPictureParams.Builder()
.setSourceRectHint(hint)
.setAspectRatio(new Rational(hint.width(), hint.height()))
.build());
return true;
}
private void initPlayer(Uri uri) {
mVideoView.setVideoURI(uri);
mVideoView.requestFocus();
mVideoView.setOnPreparedListener(mp -> {
mp.setLooping(true);
mVideoView.start();
});
}
private Rect getVideoRectHint() {
final Rect hint = new Rect();
mVideoView.getGlobalVisibleRect(hint);
return hint;
}
private void setEntryState(int gravity) {
mPositionOnEntry = gravity;
mEntryButton.setText(getString(
R.string.activity_picture_in_picture_source_rect_hint_current_position,
getGravityName(mPositionOnEntry)));
final FrameLayout.LayoutParams p = (FrameLayout.LayoutParams) mVideoView.getLayoutParams();
p.gravity = mPositionOnEntry;
mVideoView.setLayoutParams(p);
}
private void setExitState(int gravity) {
mPositionOnExit = gravity;
mExitButton.setText(getString(
R.string.activity_picture_in_picture_source_rect_hint_position_on_exit,
getGravityName(mPositionOnExit)));
}
private String getGravityName(int gravity) {
return gravity == Gravity.TOP ? "TOP" : "BOTTOM";
}
}

View File

@@ -58,7 +58,6 @@ public class FixedAspectRatioImageView extends ImageView {
height = MeasureSpec.getSize(heightMeasureSpec);
width = (int) (height * mAspectRatio.floatValue());
}
android.util.Log.d("DebugMe", "onMeasure w=" + width + " h=" + height);
setMeasuredDimension(width, height);
}
}