205 lines
7.8 KiB
Java
205 lines
7.8 KiB
Java
/*
|
|
* Copyright (C) 2020 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.startingwindow;
|
|
|
|
import android.animation.Animator;
|
|
import android.animation.AnimatorListenerAdapter;
|
|
import android.animation.AnimatorSet;
|
|
import android.animation.ObjectAnimator;
|
|
import android.animation.ValueAnimator;
|
|
import android.annotation.SuppressLint;
|
|
import android.app.Activity;
|
|
import android.content.ComponentName;
|
|
import android.content.Intent;
|
|
import android.content.pm.ShortcutInfo;
|
|
import android.content.pm.ShortcutManager;
|
|
import android.content.res.Resources;
|
|
import android.os.Build.VERSION_CODES;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.os.Process;
|
|
import android.os.SystemClock;
|
|
import android.provider.Settings.Global;
|
|
import android.util.TypedValue;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.ViewTreeObserver.OnPreDrawListener;
|
|
import android.view.animation.AccelerateInterpolator;
|
|
import android.view.animation.Interpolator;
|
|
import android.view.animation.PathInterpolator;
|
|
import android.widget.Button;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.LinearLayout.LayoutParams;
|
|
import android.window.SplashScreen;
|
|
import android.window.SplashScreenView;
|
|
|
|
import androidx.annotation.RequiresApi;
|
|
|
|
import java.time.Instant;
|
|
import java.time.temporal.ChronoUnit;
|
|
import java.util.Collections;
|
|
|
|
@RequiresApi(api = VERSION_CODES.S)
|
|
public class CustomizeExitActivity extends Activity {
|
|
|
|
public static final Interpolator EASE_IN_OUT = new PathInterpolator(.48f, .11f, .53f, .87f);
|
|
public static final Interpolator ACCELERATE = new AccelerateInterpolator();
|
|
public static final int MOCK_DELAY = 200;
|
|
public static final int MARGIN_ANIMATION_DURATION = 800;
|
|
public static final int SPLASHSCREEN_ALPHA_ANIMATION_DURATION = 500;
|
|
public static final int SPLASHSCREEN_TY_ANIMATION_DURATION = 1000;
|
|
public static final boolean WAIT_FOR_AVD_TO_FINISH = true;
|
|
public static final boolean DEBUG = false;
|
|
|
|
boolean appReady = false;
|
|
private float animationScale = 1.0f;
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
setContentView(R.layout.main_activity);
|
|
getWindow().setDecorFitsSystemWindows(false);
|
|
|
|
// On Android S, this new method has been added to Activity
|
|
SplashScreen splashScreen = getSplashScreen();
|
|
|
|
// Setting an OnExitAnimationListener on the SplashScreen indicates
|
|
// to the system that the application will handle the exit animation.
|
|
// This means that the SplashScreen will be inflated in the application
|
|
// process once the process has started.
|
|
// Otherwise, the splashscreen stays in the SystemUI process and will be
|
|
// dismissed once the first frame of the app is drawn
|
|
splashScreen.setOnExitAnimationListener(this::onSplashScreenExit);
|
|
|
|
animationScale = Global.getFloat(getContentResolver(),
|
|
Global.ANIMATOR_DURATION_SCALE, 1.0f);
|
|
|
|
// Create some artificial delay to simulate some local database fetch for example
|
|
new Handler(Looper.getMainLooper())
|
|
.postDelayed(() -> appReady = true, (long) (MOCK_DELAY * animationScale));
|
|
|
|
// We use a pre draw listener to delay the removal of the splashscreen
|
|
// until our app is ready
|
|
final View content = findViewById(android.R.id.content);
|
|
content.getViewTreeObserver().addOnPreDrawListener(
|
|
new OnPreDrawListener() {
|
|
@Override
|
|
public boolean onPreDraw() {
|
|
if (appReady) {
|
|
content.getViewTreeObserver().removeOnPreDrawListener(this);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
private void onSplashScreenExit(SplashScreenView view) {
|
|
// At this point the first frame of the application is drawn and
|
|
// the SplashScreen is ready to be removed.
|
|
|
|
// It is now up to the application to animate the provided view
|
|
// since the listener is registered
|
|
AnimatorSet animatorSet = new AnimatorSet();
|
|
animatorSet.setDuration(500);
|
|
|
|
ObjectAnimator translationY = ObjectAnimator.ofFloat(view, "translationY", 0, view.getHeight());
|
|
translationY.setInterpolator(ACCELERATE);
|
|
|
|
ObjectAnimator alpha = ObjectAnimator.ofFloat(view, "alpha", 1, 0);
|
|
alpha.setInterpolator(ACCELERATE);
|
|
|
|
// To get fancy, we'll also animate our content
|
|
ValueAnimator marginAnimator = createContentAnimation();
|
|
|
|
animatorSet.playTogether(translationY, alpha, marginAnimator);
|
|
animatorSet.addListener(new AnimatorListenerAdapter() {
|
|
@Override
|
|
public void onAnimationEnd(Animator animation) {
|
|
view.remove();
|
|
}
|
|
});
|
|
|
|
// If we want to wait for our Animated Vector Drawable to finish animating, we can compute
|
|
// the remaining time to delay the start of the exit animation
|
|
long delayMillis = Instant.now()
|
|
.until(view.getIconAnimationStart().plus(view.getIconAnimationDuration()),
|
|
ChronoUnit.MILLIS);
|
|
view.postDelayed(animatorSet::start, (long) (delayMillis * animationScale));
|
|
}
|
|
|
|
private ValueAnimator createContentAnimation() {
|
|
Resources r = getResources();
|
|
float marginStart = TypedValue.applyDimension(
|
|
TypedValue.COMPLEX_UNIT_DIP, 30, r.getDisplayMetrics()
|
|
);
|
|
|
|
float marginEnd = TypedValue.applyDimension(
|
|
TypedValue.COMPLEX_UNIT_DIP, 10, r.getDisplayMetrics()
|
|
);
|
|
|
|
ValueAnimator marginAnimator = ValueAnimator.ofFloat(marginStart, marginEnd);
|
|
marginAnimator.addUpdateListener(valueAnimator -> {
|
|
LinearLayout container = findViewById(R.id.container);
|
|
int marginTop = Math.round((Float) valueAnimator.getAnimatedValue());
|
|
for (int i = 0; i < container.getChildCount(); i++) {
|
|
View child = container.getChildAt(i);
|
|
((LayoutParams) child.getLayoutParams()).setMargins(0, marginTop, 0, 0);
|
|
}
|
|
container.requestLayout();
|
|
});
|
|
marginAnimator.setInterpolator(EASE_IN_OUT);
|
|
return marginAnimator;
|
|
}
|
|
|
|
@SuppressLint("SetTextI18n")
|
|
private void createShortcutButton() {
|
|
final Button Button = new Button(this);
|
|
Button.setText("Create shortcut");
|
|
Button.setOnClickListener((v) -> createShortcut());
|
|
addContentView(Button, new ViewGroup.LayoutParams(
|
|
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
|
}
|
|
|
|
private void createShortcut() {
|
|
ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
|
|
String shortcutId1 = "shortcutId1";
|
|
shortcutManager.removeDynamicShortcuts(Collections.singletonList(shortcutId1));
|
|
final ShortcutInfo.Builder b = new ShortcutInfo.Builder(this, shortcutId1);
|
|
final ComponentName name = new ComponentName("com.example.android.startingwindow",
|
|
"com.example.android.startingwindow.SecondActivity");
|
|
final Intent i = new Intent(Intent.ACTION_MAIN)
|
|
.setComponent(name);
|
|
ShortcutInfo shortcut = b.setShortLabel("label")
|
|
.setLongLabel("Long label")
|
|
.setIntent(i)
|
|
.setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen)
|
|
.build();
|
|
shortcutManager.addDynamicShortcuts(Collections.singletonList(shortcut));
|
|
}
|
|
|
|
@Override
|
|
protected void onPause() {
|
|
// For the sake of this demo app, we kill the app on pause so
|
|
// we see a cold start animation for each launch
|
|
super.onPause();
|
|
finishAndRemoveTask();
|
|
Process.killProcess(Process.myPid());
|
|
}
|
|
} |