├── .gitignore
├── .idea
├── .name
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── vcs.xml
└── workspace.xml
├── LICENSE
├── README.md
├── StackOverView.iml
├── app
├── app.iml
├── build.gradle
├── lint.xml
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── wirelesspienetwork
│ │ └── overviewexample
│ │ └── OverviewActivity.java
│ └── res
│ ├── layout
│ ├── recents.xml
│ └── recents_dummy.xml
│ └── values
│ └── strings.xml
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── import-summary.txt
├── lib
├── .gitignore
├── build.gradle
├── lib.iml
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── wirelesspienetwork
│ │ └── overview
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── wirelesspienetwork
│ │ └── overview
│ │ ├── misc
│ │ ├── OverviewConfiguration.java
│ │ ├── ReferenceCountedTrigger.java
│ │ └── Utilities.java
│ │ ├── model
│ │ ├── OverviewAdapter.java
│ │ └── ViewHolder.java
│ │ └── views
│ │ ├── ObjectPool.java
│ │ ├── Overview.java
│ │ ├── OverviewCard.java
│ │ ├── OverviewCardTransform.java
│ │ ├── OverviewStackView.java
│ │ ├── OverviewStackViewLayoutAlgorithm.java
│ │ ├── OverviewStackViewScroller.java
│ │ ├── OverviewStackViewTouchHandler.java
│ │ ├── SwipeHelper.java
│ │ └── ViewAnimation.java
│ └── res
│ ├── anim
│ ├── recents_launch_prev_affiliated_task_target.xml
│ ├── recents_return_to_launcher_enter.xml
│ ├── recents_return_to_launcher_exit.xml
│ ├── recents_to_launcher_enter.xml
│ ├── recents_to_launcher_exit.xml
│ ├── recents_to_search_launcher_enter.xml
│ └── recents_to_search_launcher_exit.xml
│ └── values
│ ├── alias.xml
│ ├── arrays.xml
│ ├── attrs.xml
│ ├── bools.xml
│ ├── colors.xml
│ ├── config.xml
│ ├── defaults.xml
│ ├── dimens.xml
│ ├── donottranslate.xml
│ ├── ids.xml
│ ├── integers.xml
│ ├── internal.xml
│ ├── lland_config.xml
│ ├── lland_strings.xml
│ ├── missing.xml
│ ├── strings.xml
│ └── styles.xml
├── overview.iml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 | /*/build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | StackOverView
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Android API 19 Platform
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 BOSSYAO
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # StackOverView
2 | a custom widget of android,like task manager of android 5.0.(Android 5.0 任务管理器控件)
3 |
4 |
5 |
--------------------------------------------------------------------------------
/StackOverView.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 21
5 | buildToolsVersion "21.1.2"
6 |
7 | defaultConfig {
8 | minSdkVersion 16
9 | targetSdkVersion 21
10 | applicationId "com.wirelesspienetwork.overviewexample"
11 | }
12 |
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
17 | }
18 | }
19 |
20 | dependencies {
21 | compile project(':lib')
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
21 |
22 |
28 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wirelesspienetwork/overviewexample/OverviewActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.wirelesspienetwork.overviewexample;
18 |
19 | import android.app.Activity;
20 | import android.app.SearchManager;
21 | import android.content.Context;
22 | import android.content.Intent;
23 | import android.content.IntentFilter;
24 | import android.graphics.Color;
25 | import android.os.Bundle;
26 | import android.os.Handler;
27 | import android.view.View;
28 | import android.view.ViewGroup;
29 |
30 | import com.wirelesspienetwork.overview.misc.Utilities;
31 | import com.wirelesspienetwork.overview.model.OverviewAdapter;
32 | import com.wirelesspienetwork.overview.model.ViewHolder;
33 | import com.wirelesspienetwork.overview.views.Overview;
34 |
35 | import java.lang.reflect.InvocationTargetException;
36 | import java.util.ArrayList;
37 | import java.util.Random;
38 |
39 | /**
40 | * The main Recents activity that is started from AlternateRecentsComponent.
41 | */
42 | public class OverviewActivity extends Activity implements Overview.RecentsViewCallbacks
43 | {
44 | boolean mVisible;
45 | // Top level views
46 | Overview mRecentsView;
47 |
48 | /** Called with the activity is first created. */
49 | @Override
50 | public void onCreate(Bundle savedInstanceState) {
51 | super.onCreate(savedInstanceState);
52 | // For the non-primary user, ensure that the SystemSericesProxy is initialized
53 |
54 | // Initialize the widget host (the host id is static and does not change)
55 |
56 | // Set the Recents layout
57 | setContentView(R.layout.recents);
58 | mRecentsView = (Overview) findViewById(R.id.recents_view);
59 | mRecentsView.setCallbacks(this);
60 | mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
61 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
62 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
63 |
64 | // Register the broadcast receiver to handle messages when the screen is turned off
65 | IntentFilter filter = new IntentFilter();
66 | filter.addAction(Intent.ACTION_SCREEN_OFF);
67 | filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
68 |
69 | // Private API calls to make the shadows look better
70 | try {
71 | Utilities.setShadowProperty("ambientRatio", String.valueOf(1.5f));
72 | } catch (IllegalAccessException e) {
73 | e.printStackTrace();
74 | } catch (InvocationTargetException e) {
75 | e.printStackTrace();
76 | }
77 | }
78 |
79 | @Override
80 | protected void onNewIntent(Intent intent) {
81 | super.onNewIntent(intent);
82 | setIntent(intent);
83 | }
84 |
85 | @Override
86 | protected void onStart() {
87 | super.onStart();
88 | }
89 |
90 | @Override
91 | protected void onResume() {
92 | super.onResume();
93 |
94 | // Mark Recents as visible
95 | mVisible = true;
96 |
97 | ArrayList models = new ArrayList<>();
98 | for(int i = 0; i < 10; ++i)
99 | {
100 | Random random = new Random();
101 | random.setSeed(i);
102 | int color = Color.argb(255, random.nextInt(255), random.nextInt(255), random.nextInt(255));
103 | models.add(color);
104 | }
105 |
106 | final OverviewAdapter stack = new OverviewAdapter, Integer>(models)
107 | {
108 | @Override
109 | public ViewHolder onCreateViewHolder(Context context, ViewGroup parent) {
110 | View v = View.inflate(context, R.layout.recents_dummy, null);
111 | return new ViewHolder(v);
112 | }
113 |
114 | @Override
115 | public void onBindViewHolder(ViewHolder viewHolder) {
116 | viewHolder.itemView.setBackgroundColor(viewHolder.model);
117 | }
118 | };
119 |
120 | mRecentsView.setTaskStack(stack);
121 |
122 | // new Handler().postDelayed(new Runnable() {
123 | // @Override
124 | // public void run() {
125 | // stack.notifyDataSetInserted(new Integer(1), 2);
126 | // }
127 | // },2000);
128 |
129 |
130 | }
131 |
132 | @Override
133 | protected void onStop() {
134 | super.onStop();
135 | }
136 |
137 | @Override
138 | protected void onDestroy() {
139 | super.onDestroy();
140 | }
141 |
142 | @Override
143 | public void onTrimMemory(int level) {
144 | }
145 |
146 | @Override
147 | public void onAllCardsDismissed() {
148 | }
149 |
150 | @Override
151 | public void onCardDismissed(int position) {
152 |
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recents.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
21 |
22 |
23 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recents_dummy.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
22 |
23 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | OverviewExample
4 |
5 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | jcenter()
5 | }
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:1.0.0'
8 | }
9 | }
10 |
11 | allprojects {
12 | repositories {
13 | jcenter()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Bossyao168/StackOverView/9808f07dca114479ab6f99609f8de5c87b5ce6ff/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 10 15:27:10 PDT 2013
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/import-summary.txt:
--------------------------------------------------------------------------------
1 | ECLIPSE ANDROID PROJECT IMPORT SUMMARY
2 | ======================================
3 |
4 | Ignored Files:
5 | --------------
6 | The following files were *not* copied into the new Gradle project; you
7 | should evaluate whether these are still needed in your project and if
8 | so manually move them:
9 |
10 | * Android.mk
11 | * NOTICE
12 | * proguard.flags
13 | * scripts\
14 | * scripts\copy_profile_icons.sh
15 | * scripts\new_merge.py
16 | * test\
17 | * test\Android.mk
18 | * test\AndroidManifest.xml
19 | * test\SampleTrustAgent\
20 | * test\SampleTrustAgent\Android.mk
21 | * test\SampleTrustAgent\AndroidManifest.xml
22 | * test\SampleTrustAgent\res\
23 | * test\SampleTrustAgent\res\layout\
24 | * test\SampleTrustAgent\res\layout\sample_trust_agent_settings.xml
25 | * test\SampleTrustAgent\res\values\
26 | * test\SampleTrustAgent\res\values\strings.xml
27 | * test\SampleTrustAgent\res\xml\
28 | * test\SampleTrustAgent\res\xml\sample_trust_agent.xml
29 | * test\SampleTrustAgent\src\
30 | * test\SampleTrustAgent\src\com\
31 | * test\SampleTrustAgent\src\com\android\
32 | * test\SampleTrustAgent\src\com\android\trustagent\
33 | * test\SampleTrustAgent\src\com\android\trustagent\test\
34 | * test\SampleTrustAgent\src\com\android\trustagent\test\SampleTrustAgent.java
35 | * test\SampleTrustAgent\src\com\android\trustagent\test\SampleTrustAgentSettings.java
36 | * test\res\
37 | * test\res\drawable-hdpi\
38 | * test\res\drawable-hdpi\app_icon.png
39 | * test\res\drawable-mdpi\
40 | * test\res\drawable-mdpi\app_icon.png
41 | * test\res\drawable-xhdpi\
42 | * test\res\drawable-xhdpi\app_icon.png
43 | * test\res\layout\
44 | * test\res\layout\keyguard_test_activity.xml
45 | * test\res\menu\
46 | * test\res\menu\optionmenu.xml
47 | * test\res\values\
48 | * test\res\values\strings.xml
49 | * test\src\
50 | * test\src\com\
51 | * test\src\com\android\
52 | * test\src\com\android\keyguard\
53 | * test\src\com\android\keyguard\test\
54 | * test\src\com\android\keyguard\test\KeyguardTestActivity.java
55 |
56 | Moved Files:
57 | ------------
58 | Android Gradle projects use a different directory structure than ADT
59 | Eclipse projects. Here's how the projects were restructured:
60 |
61 | * AndroidManifest.xml => keyguard\src\main\AndroidManifest.xml
62 | * res\ => keyguard\src\main\res\
63 | * src\ => keyguard\src\main\java\
64 |
65 | Next Steps:
66 | -----------
67 | You can now build the project. The Gradle project needs network
68 | connectivity to download dependencies.
69 |
70 | Bugs:
71 | -----
72 | If for some reason your project does not build, and you determine that
73 | it is due to a bug or limitation of the Eclipse to Gradle importer,
74 | please file a bug at http://b.android.com with category
75 | Component-Tools.
76 |
77 | (This import summary is for your information only, and can be deleted
78 | after import once you are satisfied with the results.)
79 |
--------------------------------------------------------------------------------
/lib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/lib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 21
5 | buildToolsVersion "21.1.2"
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 21
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(dir: 'libs', include: ['*.jar'])
23 | }
24 |
--------------------------------------------------------------------------------
/lib/lib.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/lib/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:/Android/AndroidSDK/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/lib/src/androidTest/java/com/wirelesspienetwork/overview/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.wirelesspienetwork.overview;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/lib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/misc/OverviewConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.wirelesspienetwork.overview.misc;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources;
5 | import android.graphics.Rect;
6 | import android.util.DisplayMetrics;
7 | import android.util.TypedValue;
8 | import android.view.animation.AnimationUtils;
9 | import android.view.animation.Interpolator;
10 |
11 | import com.wirelesspienetwork.overview.R;
12 |
13 | public class OverviewConfiguration {
14 |
15 | /** Interpolators */
16 | public Interpolator fastOutSlowInInterpolator;
17 | public Interpolator fastOutLinearInInterpolator;
18 | public Interpolator linearOutSlowInInterpolator;
19 | public Interpolator quintOutInterpolator;
20 |
21 | /** Insets */
22 | public Rect displayRect = new Rect();
23 |
24 | /** Task stack */
25 | public int taskStackScrollDuration;
26 | public int taskStackMaxDim;
27 | public int taskStackTopPaddingPx;
28 | public float taskStackWidthPaddingPct;
29 | public float taskStackOverscrollPct;
30 |
31 | /** Task view animation and styles */
32 | public int taskViewEnterFromHomeDelay;
33 | public int taskViewEnterFromHomeDuration;
34 | public int taskViewEnterFromHomeStaggerDelay;
35 | public int taskViewRemoveAnimDuration;
36 | public int taskViewRemoveAnimTranslationXPx;
37 | public int taskViewTranslationZMinPx;
38 | public int taskViewTranslationZMaxPx;
39 |
40 | /** Private constructor */
41 | public OverviewConfiguration(Context context) {
42 | fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
43 | android.R.interpolator.accelerate_decelerate);
44 | fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
45 | android.R.interpolator.accelerate_decelerate);
46 | linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
47 | android.R.interpolator.accelerate_decelerate);
48 | quintOutInterpolator = AnimationUtils.loadInterpolator(context,
49 | android.R.interpolator.decelerate_quint);
50 | update(context);
51 | }
52 |
53 | /** Updates the state, given the specified context */
54 | void update(Context context) {
55 | Resources res = context.getResources();
56 | DisplayMetrics dm = res.getDisplayMetrics();
57 |
58 | // Insets
59 | displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
60 |
61 | // Task stack
62 | taskStackScrollDuration =
63 | res.getInteger(R.integer.recents_animate_task_stack_scroll_duration);
64 |
65 | //获取dimen资源值
66 | TypedValue widthPaddingPctValue = new TypedValue();
67 | res.getValue(R.dimen.recents_stack_width_padding_percentage, widthPaddingPctValue, true);
68 | taskStackWidthPaddingPct = widthPaddingPctValue.getFloat();
69 |
70 | //获取dimen资源值
71 | TypedValue stackOverscrollPctValue = new TypedValue();
72 | res.getValue(R.dimen.recents_stack_overscroll_percentage, stackOverscrollPctValue, true);
73 | taskStackOverscrollPct = stackOverscrollPctValue.getFloat();
74 |
75 | taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
76 | taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding);
77 |
78 | // Task view animation and styles
79 | taskViewEnterFromHomeDelay =
80 | res.getInteger(R.integer.recents_animate_task_enter_from_home_delay);
81 | taskViewEnterFromHomeDuration =
82 | res.getInteger(R.integer.recents_animate_task_enter_from_home_duration);
83 | taskViewEnterFromHomeStaggerDelay =
84 | res.getInteger(R.integer.recents_animate_task_enter_from_home_stagger_delay);
85 | taskViewRemoveAnimDuration =
86 | res.getInteger(R.integer.recents_animate_task_view_remove_duration);
87 | taskViewRemoveAnimTranslationXPx =
88 | res.getDimensionPixelSize(R.dimen.recents_task_view_remove_anim_translation_x);
89 | taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
90 | taskViewTranslationZMaxPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
91 | }
92 | /**
93 | * Returns the task stack bounds in the current orientation. These bounds do not account for
94 | * the system insets.
95 | */
96 | public void getOverviewStackBounds(int windowWidth, int windowHeight,
97 | Rect taskStackBounds) {
98 | taskStackBounds.set(0, 64, windowWidth, windowHeight);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/misc/ReferenceCountedTrigger.java:
--------------------------------------------------------------------------------
1 | package com.wirelesspienetwork.overview.misc;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.content.Context;
6 |
7 | import java.util.ArrayList;
8 |
9 | /**
10 | * A ref counted trigger that does some logic when the count is first incremented, or last
11 | * decremented. Not thread safe as it's not currently needed.
12 | */
13 | public class ReferenceCountedTrigger {
14 |
15 | Context mContext;
16 | int mCount;
17 | ArrayList mFirstIncRunnables = new ArrayList();
18 | ArrayList mLastDecRunnables = new ArrayList();
19 | Runnable mErrorRunnable;
20 |
21 | // Convenience runnables
22 | Runnable mIncrementRunnable = new Runnable() {
23 | @Override
24 | public void run() {
25 | increment();
26 | }
27 | };
28 | Runnable mDecrementRunnable = new Runnable() {
29 | @Override
30 | public void run() {
31 | decrement();
32 | }
33 | };
34 |
35 | public ReferenceCountedTrigger(Context context, Runnable firstIncRunnable,
36 | Runnable lastDecRunnable, Runnable errorRunanable) {
37 | mContext = context;
38 | if (firstIncRunnable != null) mFirstIncRunnables.add(firstIncRunnable);
39 | if (lastDecRunnable != null) mLastDecRunnables.add(lastDecRunnable);
40 | mErrorRunnable = errorRunanable;
41 | }
42 |
43 | /** Increments the ref count */
44 | public void increment() {
45 | if (mCount == 0 && !mFirstIncRunnables.isEmpty()) {
46 | int numRunnables = mFirstIncRunnables.size();
47 | for (int i = 0; i < numRunnables; i++) {
48 | mFirstIncRunnables.get(i).run();
49 | }
50 | }
51 | mCount++;
52 | }
53 |
54 | /** Convenience method to increment this trigger as a runnable */
55 | public Runnable incrementAsRunnable() {
56 | return mIncrementRunnable;
57 | }
58 |
59 | /** Adds a runnable to the last-decrement runnables list. */
60 | public void addLastDecrementRunnable(Runnable r) {
61 | // To ensure that the last decrement always calls, we increment and decrement after setting
62 | // the last decrement runnable
63 | boolean ensureLastDecrement = (mCount == 0);
64 | if (ensureLastDecrement) increment();
65 | mLastDecRunnables.add(r);
66 | if (ensureLastDecrement) decrement();
67 | }
68 |
69 | /** Decrements the ref count */
70 | public void decrement() {
71 | mCount--;
72 | if (mCount == 0 && !mLastDecRunnables.isEmpty()) {
73 | int numRunnables = mLastDecRunnables.size();
74 | for (int i = 0; i < numRunnables; i++) {
75 | mLastDecRunnables.get(i).run();
76 | }
77 | } else if (mCount < 0) {
78 | if (mErrorRunnable != null) {
79 | mErrorRunnable.run();
80 | } else {
81 | new Throwable("Invalid ref count").printStackTrace();
82 | }
83 | }
84 | }
85 |
86 | /** Convenience method to decrement this trigger as a runnable. */
87 | public Runnable decrementAsRunnable() {
88 | return mDecrementRunnable;
89 | }
90 | /** Convenience method to decrement this trigger as a animator listener. */
91 | public Animator.AnimatorListener decrementOnAnimationEnd() {
92 | return new AnimatorListenerAdapter() {
93 | @Override
94 | public void onAnimationEnd(Animator animation) {
95 | decrement();
96 | }
97 | };
98 | }
99 |
100 | /** Returns the current ref count */
101 | public int getCount() {
102 | return mCount;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/misc/Utilities.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.wirelesspienetwork.overview.misc;
18 |
19 | import android.graphics.Matrix;
20 | import android.graphics.Rect;
21 | import android.view.View;
22 |
23 | import java.lang.reflect.InvocationTargetException;
24 | import java.lang.reflect.Method;
25 | import java.util.ArrayList;
26 |
27 | /* Common code */
28 | public class Utilities {
29 |
30 | // Reflection methods for altering shadows
31 | private static Method sPropertyMethod;
32 | static {
33 | try {
34 | Class> c = Class.forName("android.view.GLES20Canvas");
35 | sPropertyMethod = c.getDeclaredMethod("setProperty", String.class, String.class);
36 | if (!sPropertyMethod.isAccessible()) sPropertyMethod.setAccessible(true);
37 | } catch (ClassNotFoundException e) {
38 | e.printStackTrace();
39 | } catch (NoSuchMethodException e) {
40 | e.printStackTrace();
41 | }
42 | }
43 |
44 | /** Scales a rect about its centroid */
45 | public static void scaleRectAboutCenter(Rect r, float scale) {
46 | if (scale != 1.0f) {
47 | int cx = r.centerX();
48 | int cy = r.centerY();
49 | r.offset(-cx, -cy);
50 | r.left = (int) (r.left * scale + 0.5f);
51 | r.top = (int) (r.top * scale + 0.5f);
52 | r.right = (int) (r.right * scale + 0.5f);
53 | r.bottom = (int) (r.bottom * scale + 0.5f);
54 | r.offset(cx, cy);
55 | }
56 | }
57 |
58 | /** Maps a coorindate in a descendant view into the parent. */
59 | public static float mapCoordInDescendentToSelf(View descendant, View root,
60 | float[] coord, boolean includeRootScroll) {
61 | ArrayList ancestorChain = new ArrayList();
62 |
63 | float[] pt = {coord[0], coord[1]};
64 |
65 | View v = descendant;
66 | while(v != root && v != null) {
67 | ancestorChain.add(v);
68 | v = (View) v.getParent();
69 | }
70 | ancestorChain.add(root);
71 |
72 | float scale = 1.0f;
73 | int count = ancestorChain.size();
74 | for (int i = 0; i < count; i++) {
75 | View v0 = ancestorChain.get(i);
76 | // For TextViews, scroll has a meaning which relates to the text position
77 | // which is very strange... ignore the scroll.
78 | if (v0 != descendant || includeRootScroll) {
79 | pt[0] -= v0.getScrollX();
80 | pt[1] -= v0.getScrollY();
81 | }
82 |
83 | v0.getMatrix().mapPoints(pt);
84 | pt[0] += v0.getLeft();
85 | pt[1] += v0.getTop();
86 | scale *= v0.getScaleX();
87 | }
88 |
89 | coord[0] = pt[0];
90 | coord[1] = pt[1];
91 | return scale;
92 | }
93 |
94 | /** Maps a coordinate in the root to a descendent. */
95 | public static float mapCoordInSelfToDescendent(View descendant, View root,
96 | float[] coord, Matrix tmpInverseMatrix) {
97 | ArrayList ancestorChain = new ArrayList();
98 |
99 | float[] pt = {coord[0], coord[1]};
100 |
101 | View v = descendant;
102 | while(v != root) {
103 | ancestorChain.add(v);
104 | v = (View) v.getParent();
105 | }
106 | ancestorChain.add(root);
107 |
108 | float scale = 1.0f;
109 | int count = ancestorChain.size();
110 | tmpInverseMatrix.set(new Matrix());
111 | for (int i = count - 1; i >= 0; i--) {
112 | View ancestor = ancestorChain.get(i);
113 | View next = i > 0 ? ancestorChain.get(i-1) : null;
114 |
115 | pt[0] += ancestor.getScrollX();
116 | pt[1] += ancestor.getScrollY();
117 |
118 | if (next != null) {
119 | pt[0] -= next.getLeft();
120 | pt[1] -= next.getTop();
121 | next.getMatrix().invert(tmpInverseMatrix);
122 | tmpInverseMatrix.mapPoints(pt);
123 | scale *= next.getScaleX();
124 | }
125 | }
126 |
127 | coord[0] = pt[0];
128 | coord[1] = pt[1];
129 | return scale;
130 | }
131 |
132 | /** Sets some private shadow properties. */
133 | public static void setShadowProperty(String property, String value)
134 | throws IllegalAccessException, InvocationTargetException {
135 | if (sPropertyMethod != null) {
136 | sPropertyMethod.invoke(null, property, value);
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/model/OverviewAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.wirelesspienetwork.overview.model;
18 |
19 | import android.content.Context;
20 | import android.view.ViewGroup;
21 |
22 | import com.wirelesspienetwork.overview.misc.OverviewConfiguration;
23 | import com.wirelesspienetwork.overview.views.OverviewCard;
24 |
25 | import java.util.ArrayList;
26 | import java.util.List;
27 |
28 | public abstract class OverviewAdapter {
29 |
30 | /** Task stack callbacks */
31 | public interface Callbacks {
32 | public void onCardAdded(OverviewAdapter adapter, int position);
33 | public void onCardRemoved(OverviewAdapter adapter, int position);
34 | }
35 |
36 | Callbacks mCallbacks;
37 |
38 | //这个只是单纯用来计数的
39 | List mItems = new ArrayList <>();
40 |
41 | public OverviewAdapter()
42 | {
43 |
44 | }
45 |
46 | public OverviewAdapter(List models)
47 | {
48 | if (models != null) {
49 | mItems = models;
50 | }
51 | }
52 |
53 | /** Sets the callbacks for this task stack */
54 | public void setCallbacks(Callbacks cb) {
55 | mCallbacks = cb;
56 | }
57 |
58 | /**
59 | * 插入元素
60 | * @param model 元素
61 | * @param position 位置
62 | */
63 | public void notifyDataSetInserted(Model model, int position)
64 | {
65 | if (position < 0 || position > mItems.size()) {
66 | throw new IllegalArgumentException("Position is out of bounds.");
67 | }
68 |
69 | mItems.add(position, model);
70 |
71 | if (mCallbacks != null) {
72 | mCallbacks.onCardAdded(this, position);
73 | }
74 | }
75 |
76 | /** Removes a task */
77 | public void notifyDataSetRemoved(int position)
78 | {
79 | if (position < 0 || position > mItems.size()) {
80 | throw new IllegalArgumentException("Position is out of bounds.");
81 | }
82 |
83 | mItems.remove(position);
84 |
85 | if (mCallbacks != null) {
86 | // Notify that a task has been removed
87 | mCallbacks.onCardRemoved(this, position);
88 | }
89 | }
90 |
91 | /**
92 | * 只不过是删除然后再重新添加罢了,来实现改变某项内容
93 | * @param newItems
94 | */
95 | public void notifyDataSetChanged(List newItems)
96 | {
97 | if (newItems == null) {
98 | newItems = new ArrayList<>();
99 | }
100 |
101 | for(int i = 0; i < mItems.size(); ++i)
102 | {
103 | if (mCallbacks != null) {
104 | mCallbacks.onCardRemoved(this, i);
105 | }
106 | }
107 |
108 | for(int i =0; i < newItems.size(); ++i) {
109 | if (mCallbacks != null) {
110 | mCallbacks.onCardAdded(this, i);
111 | }
112 | }
113 | }
114 |
115 | public List getData() { return mItems;}
116 |
117 | public abstract VH onCreateViewHolder(Context context, ViewGroup parent);
118 |
119 | /**
120 | * This method is expected to populate the view in vh with the model in vh.
121 | */
122 | public abstract void onBindViewHolder(VH vh);
123 |
124 | public final int getNumberOfItems() {
125 | return mItems.size();
126 | }
127 |
128 | public final VH createViewHolder(Context context, OverviewConfiguration config) {
129 |
130 | OverviewCard container = new OverviewCard(context);
131 | container.setConfig(config);
132 | VH vh = onCreateViewHolder(context, container);
133 | vh.setContainer(container);
134 | return vh;
135 | }
136 |
137 | public final void bindViewHolder(VH vh, int position) {
138 | vh.model = mItems.get(position);
139 | onBindViewHolder(vh);
140 | }
141 | }
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/model/ViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.wirelesspienetwork.overview.model;
2 |
3 | import android.view.View;
4 |
5 | import com.wirelesspienetwork.overview.views.OverviewCard;
6 |
7 | public class ViewHolder
8 | {
9 | public final V itemView;
10 | public Model model;
11 |
12 | private OverviewCard mContainer;
13 |
14 | private int mCurrentPosition = -1;
15 | private int mLastPosition = -1;
16 |
17 | public ViewHolder(V view)
18 | {
19 | this.itemView = view;
20 | }
21 |
22 | public void setPosition(int position) {
23 | mLastPosition = mCurrentPosition;
24 | mCurrentPosition = position;
25 | }
26 |
27 | public int getPosition() {
28 | return mCurrentPosition;
29 | }
30 |
31 | public int getLastPosition() {
32 | return mLastPosition;
33 | }
34 |
35 | public OverviewCard getContainer()
36 | {
37 | return mContainer;
38 | }
39 |
40 | protected void setContainer(OverviewCard container) {
41 | if (mContainer != null) {
42 | mContainer.setContent(null);
43 | }
44 | mContainer = container;
45 | if (mContainer != null && itemView != null) {
46 | mContainer.setContent(itemView);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/views/ObjectPool.java:
--------------------------------------------------------------------------------
1 | package com.wirelesspienetwork.overview.views;
2 |
3 | import android.content.Context;
4 |
5 | import java.util.Iterator;
6 | import java.util.LinkedList;
7 |
8 | public class ObjectPool {
9 |
10 | public interface ObjectPoolConsumer {
11 | public V createObject(Context context);
12 | public void prepareObjectToEnterPool(V v);
13 | public void prepareObjectToLeavePool(V v, T prepareData, boolean isNewObject);
14 | public boolean hasPreferredData(V v, T preferredData);
15 | }
16 |
17 | Context mContext;
18 | ObjectPoolConsumer mObjectCreator;
19 | LinkedList mPool = new LinkedList();
20 |
21 | /** Initializes the pool with a fixed predetermined pool size */
22 | public ObjectPool(Context context, ObjectPoolConsumer objectCreator) {
23 | mContext = context;
24 | mObjectCreator = objectCreator;
25 | }
26 |
27 | /** Returns a view into the pool */
28 | void returnObjectToPool(V v) {
29 | mObjectCreator.prepareObjectToEnterPool(v);
30 | mPool.push(v);
31 | }
32 |
33 | /** Gets a view from the pool and prepares it */
34 | V pickUpObjectFromPool(T preferredData, T prepareData) {
35 | V v = null;
36 | boolean isNewObject = false;
37 | if (mPool.isEmpty()) {
38 | v = mObjectCreator.createObject(mContext);
39 | isNewObject = true;
40 | } else {
41 | // Try and find a preferred view
42 | Iterator iter = mPool.iterator();
43 | while (iter.hasNext()) {
44 | V vpv = iter.next();
45 | if (mObjectCreator.hasPreferredData(vpv, preferredData)) {
46 | v = vpv;
47 | iter.remove();
48 | break;
49 | }
50 | }
51 | // Otherwise, just grab the first view
52 | if (v == null) {
53 | v = mPool.pop();
54 | }
55 | }
56 | mObjectCreator.prepareObjectToLeavePool(v, prepareData, isNewObject);
57 | return v;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/views/Overview.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.wirelesspienetwork.overview.views;
18 |
19 | import android.annotation.TargetApi;
20 | import android.content.Context;
21 | import android.graphics.Rect;
22 | import android.util.AttributeSet;
23 | import android.view.ViewGroup;
24 | import android.widget.FrameLayout;
25 |
26 | import com.wirelesspienetwork.overview.misc.OverviewConfiguration;
27 | import com.wirelesspienetwork.overview.misc.ReferenceCountedTrigger;
28 | import com.wirelesspienetwork.overview.model.OverviewAdapter;
29 |
30 | public class Overview extends FrameLayout implements OverviewStackView.Callbacks {
31 |
32 | public interface RecentsViewCallbacks {
33 | public void onCardDismissed(int position);
34 | public void onAllCardsDismissed();
35 | }
36 |
37 | OverviewStackView mStackView;
38 | OverviewConfiguration mConfig;
39 | OverviewAdapter mAdapter;
40 | RecentsViewCallbacks mCallbacks;
41 |
42 | public Overview(Context context) {
43 | super(context);
44 | init(context);
45 | }
46 |
47 | public Overview(Context context, AttributeSet attrs) {
48 | super(context, attrs);
49 | init(context);
50 | }
51 |
52 | public Overview(Context context, AttributeSet attrs, int defStyleAttr) {
53 | super(context, attrs, defStyleAttr);
54 | init(context);
55 | }
56 |
57 | @TargetApi(21)
58 | public Overview(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
59 | super(context, attrs, defStyleAttr, defStyleRes);
60 | init(context);
61 | }
62 |
63 | private void init(Context context)
64 | {
65 | mConfig = new OverviewConfiguration(context);
66 | }
67 |
68 | /** Sets the callbacks */
69 | public void setCallbacks(RecentsViewCallbacks cb) {
70 | mCallbacks = cb;
71 | }
72 |
73 | /** Set/get the bsp root node */
74 | public void setTaskStack(OverviewAdapter adapter) {
75 |
76 | if (mStackView != null) {
77 | removeView(mStackView);
78 | }
79 |
80 | mAdapter = adapter;
81 | mStackView = new OverviewStackView(getContext(), adapter, mConfig);
82 | LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
83 | mStackView.setLayoutParams(params);
84 |
85 | mStackView.setCallbacks(this);
86 | mStackView.setAlpha(0);
87 | mStackView.animate().alpha(1.f).setStartDelay(2000).setDuration(3500).start();
88 |
89 | //所以说 OverviewStackView 才是重点
90 | addView(mStackView);
91 | }
92 |
93 | /**
94 | * This is called with the full size of the window since we are handling our own insets.
95 | */
96 | @Override
97 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
98 |
99 | int width = MeasureSpec.getSize(widthMeasureSpec);
100 | int height = MeasureSpec.getSize(heightMeasureSpec);
101 |
102 | if (mStackView != null) {
103 | Rect stackBounds = new Rect();
104 | mConfig.getOverviewStackBounds(width, height, stackBounds);
105 | mStackView.setStackInsetRect(stackBounds);
106 | }
107 |
108 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
109 | }
110 |
111 | @Override
112 | public void onCardDismissed(int position) {
113 | if (mCallbacks != null) {
114 | mCallbacks.onCardDismissed(position);
115 | }
116 | }
117 |
118 | @Override
119 | public void onAllCardsDismissed() {
120 | if (mCallbacks != null) {
121 | mCallbacks.onAllCardsDismissed();
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/views/OverviewCard.java:
--------------------------------------------------------------------------------
1 | package com.wirelesspienetwork.overview.views;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ObjectAnimator;
5 | import android.animation.ValueAnimator;
6 | import android.annotation.TargetApi;
7 | import android.content.Context;
8 | import android.graphics.*;
9 | import android.util.AttributeSet;
10 | import android.view.Gravity;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.FrameLayout;
14 | import android.widget.LinearLayout;
15 |
16 | import com.wirelesspienetwork.overview.R;
17 | import com.wirelesspienetwork.overview.misc.OverviewConfiguration;
18 |
19 | /* A task view */
20 | public class OverviewCard extends FrameLayout {
21 |
22 | OverviewConfiguration mConfig;
23 |
24 | float mTaskProgress;
25 | ObjectAnimator mTaskProgressAnimator;
26 | LinearLayout mContentContainer;
27 | View mContent;
28 |
29 | // Optimizations
30 | ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
31 | new ValueAnimator.AnimatorUpdateListener() {
32 | @Override
33 | public void onAnimationUpdate(ValueAnimator animation) {
34 | setTaskProgress((Float) animation.getAnimatedValue());
35 | }
36 | };
37 |
38 |
39 | public OverviewCard(Context context) {
40 | super(context);
41 | init(context);
42 | }
43 |
44 | public OverviewCard(Context context, AttributeSet attrs) {
45 | super(context, attrs);
46 | init(context);
47 | }
48 |
49 | public OverviewCard(Context context, AttributeSet attrs, int defStyleAttr) {
50 | super(context, attrs, defStyleAttr);
51 | init(context);
52 | }
53 |
54 | @TargetApi(21)
55 | public OverviewCard(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
56 | super(context, attrs, defStyleAttr, defStyleRes);
57 | init(context);
58 | }
59 |
60 | private void init(Context context)
61 | {
62 | setBackgroundColor(Color.TRANSPARENT);
63 |
64 | mContentContainer = new LinearLayout(context);
65 | mContentContainer.setOrientation(LinearLayout.VERTICAL);
66 | LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
67 | params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
68 | mContentContainer.setLayoutParams(params);
69 | addView(mContentContainer);
70 | }
71 |
72 | //将子view的宽高设置入此父view
73 | @Override
74 | public void getHitRect(Rect outRect)
75 | {
76 | Rect contentRect = new Rect();
77 | mContent.getHitRect(contentRect);
78 | super.getHitRect(outRect);
79 | outRect.left += contentRect.left;
80 | outRect.top += contentRect.top;
81 | outRect.right = outRect.left + contentRect.width();
82 | outRect.bottom = outRect.top + contentRect.height();
83 | }
84 |
85 | public void setConfig(OverviewConfiguration config)
86 | {
87 | mConfig = config;
88 | }
89 |
90 | public void setContent(View content)
91 | {
92 | mContent = content;
93 | mContentContainer.removeAllViews();
94 | if (mContent != null) {
95 | mContentContainer.addView(mContent);
96 | }
97 | setTaskProgress(getTaskProgress());
98 | }
99 |
100 | @Override
101 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
102 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
103 | int width = MeasureSpec.getSize(widthMeasureSpec);
104 | int height = MeasureSpec.getSize(heightMeasureSpec);
105 |
106 | int widthWithoutPadding = width - getPaddingLeft() - getPaddingRight();
107 | int heightWithoutPadding = height - getPaddingTop() - getPaddingBottom();
108 |
109 | // Measure the content
110 | mContentContainer.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
111 | MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY));
112 |
113 | setMeasuredDimension(width, height);
114 | }
115 |
116 | /** Synchronizes this view's properties with the task's transform */
117 | void updateViewPropertiesToCardTransform(OverviewCardTransform toTransform, int duration) {
118 | updateViewPropertiesToCardTransform(toTransform, duration, null);
119 | }
120 |
121 | void updateViewPropertiesToCardTransform(OverviewCardTransform toTransform, int duration,
122 | ValueAnimator.AnimatorUpdateListener updateCallback) {
123 | // Apply the transform
124 | toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false,
125 | true, updateCallback);
126 |
127 | // Update the task progress
128 | if (mTaskProgressAnimator != null) {
129 | mTaskProgressAnimator.removeAllListeners();
130 | mTaskProgressAnimator.cancel();
131 | }
132 | if (duration <= 0) {
133 | setTaskProgress(toTransform.p);
134 | } else {
135 | mTaskProgressAnimator = ObjectAnimator.ofFloat(this, "taskProgress", toTransform.p);
136 | mTaskProgressAnimator.setDuration(duration);
137 | mTaskProgressAnimator.addUpdateListener(mUpdateDimListener);
138 | mTaskProgressAnimator.start();
139 | }
140 | }
141 |
142 | /** Resets this view's properties */
143 | void resetViewProperties() {
144 | OverviewCardTransform.reset(this);
145 | }
146 |
147 | /** Prepares this task view for the enter-recents animations. This is called earlier in the
148 | * first layout because the actual animation into recents may take a long time. */
149 | void prepareEnterRecentsAnimation() {
150 | }
151 |
152 | /** Animates this task view as it enters recents */
153 | void startEnterRecentsAnimation(final ViewAnimation.OverviewCardEnterContext ctx) {
154 | final OverviewCardTransform transform = ctx.currentTaskTransform;
155 |
156 | // Animate the tasks up
157 | int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
158 | int duration = Math.max(0, mConfig.taskViewEnterFromHomeDuration +
159 | frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay);
160 |
161 | int delay = Math.max(0, mConfig.taskViewEnterFromHomeDelay -
162 | frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay);
163 |
164 | setScaleX(transform.scale);
165 | setScaleY(transform.scale);
166 | animate()
167 | .translationY(transform.translationY)
168 | .setStartDelay(delay)
169 | .setInterpolator(mConfig.quintOutInterpolator)
170 | .setDuration(duration)
171 | .setListener(new Animator.AnimatorListener() {
172 | @Override
173 | public void onAnimationStart(Animator animation) {
174 |
175 | }
176 |
177 | @Override
178 | public void onAnimationEnd(Animator animation) {
179 | ctx.postAnimationTrigger.decrement();
180 | }
181 |
182 | @Override
183 | public void onAnimationCancel(Animator animation) {
184 |
185 | }
186 |
187 | @Override
188 | public void onAnimationRepeat(Animator animation) {
189 |
190 | }
191 | })
192 | .start();
193 | ctx.postAnimationTrigger.increment();
194 | }
195 |
196 | /** Sets the current task progress. */
197 | public void setTaskProgress(float p) {
198 | mTaskProgress = p;;
199 | }
200 |
201 | /** Returns the current task progress. */
202 | public float getTaskProgress() {
203 | return mTaskProgress;
204 | }
205 |
206 | /** Enables/disables handling touch on this task view. */
207 | void setTouchEnabled(boolean enabled) {
208 | setOnClickListener(!enabled ? null : new OnClickListener() {
209 | @Override
210 | public void onClick(View v) {
211 |
212 | }
213 | });
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/views/OverviewCardTransform.java:
--------------------------------------------------------------------------------
1 | package com.wirelesspienetwork.overview.views;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.graphics.Rect;
5 | import android.view.View;
6 | import android.view.ViewPropertyAnimator;
7 | import android.view.animation.Interpolator;
8 |
9 |
10 | /* The transform state for a task view */
11 | public class OverviewCardTransform {
12 | public int startDelay = 0;
13 | public int translationY = 0;
14 | public float translationZ = 0;
15 | public float scale = 1f;
16 | public float alpha = 1f;
17 | public boolean visible = false;
18 | public Rect rect = new Rect();
19 | float p = 0f;
20 |
21 | public OverviewCardTransform() {
22 | // Do nothing
23 | }
24 |
25 | public OverviewCardTransform(OverviewCardTransform o) {
26 | startDelay = o.startDelay;
27 | translationY = o.translationY;
28 | translationZ = o.translationZ;
29 | scale = o.scale;
30 | alpha = o.alpha;
31 | visible = o.visible;
32 | rect.set(o.rect);
33 | p = o.p;
34 | }
35 |
36 | /** Resets the current transform */
37 | public void reset() {
38 | startDelay = 0;
39 | translationY = 0;
40 | translationZ = 0;
41 | scale = 1f;
42 | alpha = 1f;
43 | visible = false;
44 | rect.setEmpty();
45 | p = 0f;
46 | }
47 |
48 | /** Convenience functions to compare against current property values */
49 | public boolean hasAlphaChangedFrom(float v) {
50 | return (Float.compare(alpha, v) != 0);
51 | }
52 | public boolean hasScaleChangedFrom(float v) {
53 | return (Float.compare(scale, v) != 0);
54 | }
55 | public boolean hasTranslationYChangedFrom(float v) {
56 | return (Float.compare(translationY, v) != 0);
57 | }
58 | public boolean hasTranslationZChangedFrom(float v) {
59 | return (Float.compare(translationZ, v) != 0);
60 | }
61 |
62 | /** Applies this transform to a view. */
63 | public void applyToTaskView(View v, int duration, Interpolator interp, boolean allowLayers,
64 | boolean allowShadows, ValueAnimator.AnimatorUpdateListener updateCallback) {
65 | // Check to see if any properties have changed, and update the task view
66 | if (duration > 0) {
67 | ViewPropertyAnimator anim = v.animate();
68 | boolean requiresLayers = false;
69 |
70 | // Animate to the final state
71 | if (hasTranslationYChangedFrom(v.getTranslationY())) {
72 | anim.translationY(translationY);
73 | }
74 | if (hasScaleChangedFrom(v.getScaleX())) {
75 | anim.scaleX(scale)
76 | .scaleY(scale);
77 | requiresLayers = true;
78 | }
79 | if (hasAlphaChangedFrom(v.getAlpha())) {
80 | // Use layers if we animate alpha
81 | anim.alpha(alpha);
82 | requiresLayers = true;
83 | }
84 | if (requiresLayers && allowLayers) {
85 | anim.withLayer();
86 | }
87 | anim.setStartDelay(startDelay)
88 | .setDuration(duration)
89 | .setInterpolator(interp)
90 | .start();
91 | } else {
92 | // Set the changed properties
93 | if (hasTranslationYChangedFrom(v.getTranslationY())) {
94 | v.setTranslationY(translationY);
95 | }
96 | if (hasScaleChangedFrom(v.getScaleX())) {
97 | v.setScaleX(scale);
98 | v.setScaleY(scale);
99 | }
100 | if (hasAlphaChangedFrom(v.getAlpha())) {
101 | v.setAlpha(alpha);
102 | }
103 | }
104 | }
105 |
106 | /** Reset the transform on a view. */
107 | public static void reset(View v) {
108 | v.setTranslationX(0f);
109 | v.setTranslationY(0f);
110 | v.setScaleX(1f);
111 | v.setScaleY(1f);
112 | v.setAlpha(1f);
113 | }
114 |
115 | @Override
116 | public String toString() {
117 | return "TaskViewTransform delay: " + startDelay + " y: " + translationY + " z: " + translationZ +
118 | " scale: " + scale + " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
119 | " p: " + p;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/views/OverviewStackView.java:
--------------------------------------------------------------------------------
1 | package com.wirelesspienetwork.overview.views;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.content.Context;
5 | import android.graphics.Rect;
6 | import android.os.Build;
7 | import android.os.Handler;
8 | import android.os.Looper;
9 | import android.view.LayoutInflater;
10 | import android.view.MotionEvent;
11 | import android.view.View;
12 | import android.widget.FrameLayout;
13 | import com.wirelesspienetwork.overview.misc.OverviewConfiguration;
14 | import com.wirelesspienetwork.overview.model.OverviewAdapter;
15 | import com.wirelesspienetwork.overview.model.ViewHolder;
16 |
17 | import java.util.ArrayList;
18 | import java.util.HashMap;
19 | import java.util.Map;
20 |
21 | /* The visual representation of a task stack view */
22 | public class OverviewStackView extends FrameLayout implements OverviewAdapter.Callbacks, OverviewStackViewScroller.Callbacks,
23 | ObjectPool.ObjectPoolConsumer {
24 |
25 | /** The TaskView callbacks */
26 | interface Callbacks {
27 | public void onCardDismissed(int position);
28 | public void onAllCardsDismissed();
29 | }
30 |
31 | OverviewConfiguration mConfig;
32 |
33 | OverviewAdapter mStack;
34 | OverviewStackViewLayoutAlgorithm mLayoutAlgorithm;
35 | OverviewStackViewScroller mStackScroller;
36 | OverviewStackViewTouchHandler mTouchHandler;
37 | Callbacks mCb;
38 | ObjectPool mViewPool;
39 | ArrayList mCurrentCardTransforms = new ArrayList();
40 | HashMap mViewHolderMap = new HashMap<>();
41 |
42 | Rect mOverviewStackBounds = new Rect();
43 |
44 | // Optimizations
45 | int mStackViewsAnimationDuration;
46 | boolean mStackViewsDirty = true;
47 | boolean mStackViewsClipDirty = true;
48 | boolean mAwaitingFirstLayout = true;
49 | boolean mStartEnterAnimationRequestedAfterLayout;
50 | boolean mStartEnterAnimationCompleted;
51 | ViewAnimation.OverviewCardEnterContext mStartEnterAnimationContext;
52 | int[] mTmpVisibleRange = new int[2];
53 | Rect mTmpRect = new Rect();
54 | OverviewCardTransform mTmpTransform = new OverviewCardTransform();
55 | LayoutInflater mInflater;
56 |
57 | ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
58 | new ValueAnimator.AnimatorUpdateListener() {
59 | @Override
60 | public void onAnimationUpdate(ValueAnimator animation) {
61 | requestUpdateStackViewsClip();
62 | }
63 | };
64 |
65 | public OverviewStackView(Context context, OverviewAdapter adapter, OverviewConfiguration config) {
66 | super(context);
67 | mConfig = config;
68 | mStack = adapter;
69 | mStack.setCallbacks(this);
70 | mViewPool = new ObjectPool<>(context, this);
71 | mInflater = LayoutInflater.from(context);
72 | mLayoutAlgorithm = new OverviewStackViewLayoutAlgorithm(mConfig);
73 | mStackScroller = new OverviewStackViewScroller(context, mConfig, mLayoutAlgorithm);
74 | mStackScroller.setCallbacks(this);
75 | mTouchHandler = new OverviewStackViewTouchHandler(context, this, mConfig, mStackScroller);
76 | }
77 |
78 | /** Sets the callbacks */
79 | void setCallbacks(Callbacks cb) {
80 | mCb = cb;
81 | }
82 |
83 | /** Requests that the views be synchronized with the model */
84 | void requestSynchronizeStackViewsWithModel() {
85 | requestSynchronizeStackViewsWithModel(0);
86 | }
87 | void requestSynchronizeStackViewsWithModel(int duration) {
88 | if (!mStackViewsDirty) {
89 | invalidate();
90 | mStackViewsDirty = true;
91 | }
92 | if (mAwaitingFirstLayout) {
93 | // Skip the animation if we are awaiting first layout
94 | mStackViewsAnimationDuration = 0;
95 | } else {
96 | mStackViewsAnimationDuration = Math.max(mStackViewsAnimationDuration, duration);
97 | }
98 | }
99 |
100 | /** Requests that the views clipping be updated. */
101 | void requestUpdateStackViewsClip() {
102 | if (!mStackViewsClipDirty) {
103 | invalidate();
104 | mStackViewsClipDirty = true;
105 | }
106 | }
107 |
108 | public OverviewCard getChildViewForIndex(int index) {
109 | int childCount = getChildCount();
110 | for (int i = 0; i < childCount; i++) {
111 | OverviewCard tv = (OverviewCard) getChildAt(i);
112 | ViewHolder holder = mViewHolderMap.get(tv);
113 | if (holder != null && holder.getPosition() == index) {
114 | return tv;
115 | }
116 | }
117 | return null;
118 | }
119 |
120 | private boolean updateStackTransforms(ArrayList cardTransforms,
121 | int itemCount,
122 | float stackScroll,
123 | int[] visibleRangeOut,
124 | boolean boundTranslationsToRect) {
125 | // XXX: We should be intelligent about where to look for the visible stack range using the
126 | // current stack scroll.
127 | // XXX: We should log extra cases like the ones below where we don't expect to hit very often
128 | // XXX: Print out approximately how many indices we have to go through to find the first visible transform
129 |
130 | int transformCount = cardTransforms.size();
131 | int frontMostVisibleIndex = -1;
132 | int backMostVisibleIndex = -1;
133 |
134 | // We can reuse the card transforms where possible to reduce object allocation
135 | if (transformCount < itemCount) {
136 | // If there are less transforms than cards, then add as many transforms as necessary
137 | for (int i = transformCount; i < itemCount; i++) {
138 | cardTransforms.add(new OverviewCardTransform());
139 | }
140 | } else if (transformCount > itemCount) {
141 | // If there are more transforms than cards, then just subset the transform list
142 | cardTransforms.subList(0, itemCount);
143 | }
144 |
145 | // Update the stack transforms
146 | OverviewCardTransform prevTransform = null;
147 | for (int i = itemCount - 1; i >= 0; i--) {
148 |
149 | // 这里将空的 OverviewCardTransform 丢进去
150 | OverviewCardTransform transform = mLayoutAlgorithm.getStackTransform(i,
151 | stackScroll, cardTransforms.get(i), prevTransform);
152 | if (transform.visible) {
153 | if (frontMostVisibleIndex < 0) {
154 | frontMostVisibleIndex = i;
155 | }
156 | backMostVisibleIndex = i;
157 | } else {
158 | if (backMostVisibleIndex != -1) {
159 | // We've reached the end of the visible range, so going down the rest of the
160 | // stack, we can just reset the transforms accordingly
161 | while (i >= 0) {
162 | cardTransforms.get(i).reset();
163 | i--;
164 | }
165 | break;
166 | }
167 | }
168 |
169 | if (boundTranslationsToRect) {
170 | transform.translationY = Math.min(transform.translationY,
171 | mLayoutAlgorithm.mViewRect.bottom);
172 | }
173 | prevTransform = transform;
174 | }
175 | if (visibleRangeOut != null) {
176 | visibleRangeOut[0] = frontMostVisibleIndex;
177 | visibleRangeOut[1] = backMostVisibleIndex;
178 | }
179 | return frontMostVisibleIndex != -1 && backMostVisibleIndex != -1;
180 | }
181 |
182 | /** Synchronizes the views with the model */
183 | boolean synchronizeStackViewsWithModel() {
184 | if (mStackViewsDirty) {
185 |
186 | float stackScroll = mStackScroller.getStackScroll();
187 | int[] visibleRange = mTmpVisibleRange;
188 | boolean isValidVisibleRange = updateStackTransforms(mCurrentCardTransforms, mStack.getNumberOfItems(),
189 | stackScroll, visibleRange, false);
190 |
191 | ArrayList> entrySet = new ArrayList<>(mViewHolderMap.entrySet());
192 |
193 | Map reusedMap = new HashMap<>();
194 |
195 | for(Map.Entry entry : entrySet)
196 | {
197 | int position = entry.getValue().getPosition();
198 | if (visibleRange[1] <= position && position <= visibleRange[0])
199 | {
200 | ViewHolder vh = entry.getValue();
201 | reusedMap.put(position, vh);
202 | } else {
203 | mViewPool.returnObjectToPool(entry.getValue());
204 | }
205 | }
206 |
207 | // Pick up all the newly visible children and update all the existing children
208 | for (int i = visibleRange[0]; isValidVisibleRange && i >= visibleRange[1]; i--) {
209 | OverviewCardTransform transform = mCurrentCardTransforms.get(i);
210 |
211 | ViewHolder vh = reusedMap.get(i);
212 | if (vh == null) {
213 | vh = mViewPool.pickUpObjectFromPool(i, i);
214 |
215 | if (mStackViewsAnimationDuration > 0) {
216 | // For items in the list, put them in start animating them from the
217 | // approriate ends of the list where they are expected to appear
218 | if (Float.compare(transform.p, 0f) <= 0) {
219 | mLayoutAlgorithm.getStackTransform(0f, 0f, mTmpTransform, null);
220 | } else {
221 | mLayoutAlgorithm.getStackTransform(1f, 0f, mTmpTransform, null);
222 | }
223 | vh.getContainer().updateViewPropertiesToCardTransform(mTmpTransform, 0);
224 | }
225 | }
226 |
227 | // Animate the card into place
228 | vh.getContainer().updateViewPropertiesToCardTransform(mCurrentCardTransforms.get(i),
229 | mStackViewsAnimationDuration, mRequestUpdateClippingListener);
230 | }
231 |
232 | // Reset the request-synchronize params
233 | mStackViewsAnimationDuration = 0;
234 | mStackViewsDirty = false;
235 | mStackViewsClipDirty = true;
236 | return true;
237 | }
238 | return false;
239 | }
240 |
241 | /** Updates the clip for each of the task views. */
242 | void clipTaskViews() {
243 | mStackViewsClipDirty = false;
244 | }
245 |
246 | /** The stack insets to apply to the stack contents */
247 | public void setStackInsetRect(Rect r) {
248 | mOverviewStackBounds.set(r);
249 | }
250 |
251 | @Override
252 | public void setOnTouchListener(OnTouchListener l) {
253 | super.setOnTouchListener(l);
254 | }
255 |
256 | /** Updates the min and max virtual scroll bounds */
257 | void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
258 | // Compute the min and max scroll values
259 | mLayoutAlgorithm.computeMinMaxScroll(mStack.getNumberOfItems());
260 |
261 | // Debug logging
262 | if (boundScrollToNewMinMax) {
263 | mStackScroller.boundScroll();
264 | }
265 | }
266 |
267 | @Override
268 | public boolean onInterceptTouchEvent(MotionEvent ev) {
269 | return mTouchHandler.onInterceptTouchEvent(ev);
270 | }
271 |
272 | @Override
273 | public boolean onTouchEvent(MotionEvent ev) {
274 | return mTouchHandler.onTouchEvent(ev);
275 | }
276 |
277 | @Override
278 | public void computeScroll() {
279 | mStackScroller.computeScroll();
280 | // Synchronize the views
281 | synchronizeStackViewsWithModel();
282 | clipTaskViews();
283 | }
284 |
285 | /** Computes the stack and task rects */
286 | public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
287 | // Compute the rects in the stack algorithm
288 | mLayoutAlgorithm.computeRects(windowWidth, windowHeight, taskStackBounds);
289 |
290 | // Update the scroll bounds
291 | updateMinMaxScroll(false);
292 | }
293 |
294 | /**
295 | * This is called with the full window width and height to allow stack view children to
296 | * perform the full screen transition down.
297 | */
298 | @Override
299 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
300 | int width = MeasureSpec.getSize(widthMeasureSpec);
301 | int height = MeasureSpec.getSize(heightMeasureSpec);
302 |
303 | //空间大部分的初始化都在这里
304 |
305 | // Compute our stack/task rects
306 | Rect taskStackBounds = new Rect(mOverviewStackBounds);
307 | computeRects(width, height, taskStackBounds);
308 |
309 | // If this is the first layout, then scroll to the front of the stack and synchronize the
310 | // stack views immediately to load all the views
311 | if (mAwaitingFirstLayout) {
312 | mStackScroller.setStackScrollToInitialState();
313 | requestSynchronizeStackViewsWithModel();
314 | synchronizeStackViewsWithModel();
315 | }
316 |
317 | // Measure each of the TaskViews
318 | int childCount = getChildCount();
319 | for (int i = 0; i < childCount; i++) {
320 | OverviewCard tv = (OverviewCard) getChildAt(i);
321 | if (tv.getBackground() != null) {
322 | tv.getBackground().getPadding(mTmpRect);
323 | } else {
324 | mTmpRect.setEmpty();
325 | }
326 | tv.measure(
327 | MeasureSpec.makeMeasureSpec(
328 | mLayoutAlgorithm.mTaskRect.width() + mTmpRect.left + mTmpRect.right,
329 | MeasureSpec.EXACTLY),
330 | MeasureSpec.makeMeasureSpec(
331 | mLayoutAlgorithm.mTaskRect.height() + mTmpRect.top + mTmpRect.bottom, MeasureSpec.EXACTLY));
332 | }
333 |
334 | setMeasuredDimension(width, height);
335 | }
336 |
337 | /**
338 | * This is called with the size of the space not including the top or right insets, or the
339 | * search bar height in portrait (but including the search bar width in landscape, since we want
340 | * to draw under it.
341 | */
342 | @Override
343 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
344 | // Layout each of the children
345 | int childCount = getChildCount();
346 | for (int i = 0; i < childCount; i++) {
347 | OverviewCard tv = (OverviewCard) getChildAt(i);
348 | if (tv.getBackground() != null) {
349 | tv.getBackground().getPadding(mTmpRect);
350 | } else {
351 | mTmpRect.setEmpty();
352 | }
353 | tv.layout(mLayoutAlgorithm.mTaskRect.left - mTmpRect.left,
354 | mLayoutAlgorithm.mTaskRect.top - mTmpRect.top,
355 | mLayoutAlgorithm.mTaskRect.right + mTmpRect.right,
356 | mLayoutAlgorithm.mTaskRect.bottom + mTmpRect.bottom);
357 | }
358 |
359 | if (mAwaitingFirstLayout) {
360 | mAwaitingFirstLayout = false;
361 | onFirstLayout();
362 | }
363 | }
364 |
365 | /** Handler for the first layout. */
366 | void onFirstLayout() {
367 | int offscreenY = mLayoutAlgorithm.mViewRect.bottom -
368 | (mLayoutAlgorithm.mTaskRect.top - mLayoutAlgorithm.mViewRect.top);
369 |
370 | for (Map.Entry entry : mViewHolderMap.entrySet())
371 | {
372 | entry.getKey().prepareEnterRecentsAnimation();
373 | }
374 |
375 | // If the enter animation started already and we haven't completed a layout yet, do the
376 | // enter animation now
377 | if (mStartEnterAnimationRequestedAfterLayout) {
378 | startEnterRecentsAnimation(mStartEnterAnimationContext);
379 | mStartEnterAnimationRequestedAfterLayout = false;
380 | mStartEnterAnimationContext = null;
381 | }
382 | }
383 |
384 | /** Requests this task stacks to start it's enter-recents animation */
385 | public void startEnterRecentsAnimation(ViewAnimation.OverviewCardEnterContext ctx) {
386 | // If we are still waiting to layout, then just defer until then
387 | if (mAwaitingFirstLayout) {
388 | mStartEnterAnimationRequestedAfterLayout = true;
389 | mStartEnterAnimationContext = ctx;
390 | return;
391 | }
392 |
393 | int childCount = getChildCount();
394 |
395 | if (mStack.getNumberOfItems() > 0) {
396 | // Find the launch target task
397 |
398 | int launchTargetIndex = childCount == 0 ? -1 : 0;
399 |
400 | for(int i = 0; i < childCount; ++i)
401 | {
402 | OverviewCard card = (OverviewCard)getChildAt(i);
403 | ViewHolder holder = mViewHolderMap.get(card);
404 |
405 | ctx.currentTaskTransform = new OverviewCardTransform();
406 | ctx.currentStackViewIndex = i;
407 | ctx.currentStackViewCount = childCount;
408 | ctx.currentTaskRect = mLayoutAlgorithm.mTaskRect;
409 | ctx.currentTaskOccludesLaunchTarget = (launchTargetIndex != -1);
410 | ctx.updateListener = mRequestUpdateClippingListener;
411 | mLayoutAlgorithm.getStackTransform(i, mStackScroller.getStackScroll(), ctx.currentTaskTransform, null);
412 | card.startEnterRecentsAnimation(ctx);
413 | }
414 |
415 | // Add a runnable to the post animation ref counter to clear all the views
416 | ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
417 | @Override
418 | public void run() {
419 | mStartEnterAnimationCompleted = true;
420 | }
421 | });
422 | }
423 | }
424 |
425 | public boolean isTransformedTouchPointInView(float x, float y, View child) {
426 | final Rect frame = new Rect();
427 | child.getHitRect(frame);
428 | return frame.contains((int)x, (int)y);
429 | }
430 |
431 | public void onCardAdded(OverviewAdapter stack, int position) {
432 | requestSynchronizeStackViewsWithModel();
433 | }
434 |
435 | public void onCardRemoved(OverviewAdapter stack, int removedTask) {
436 | // Remove the view associated with this task, we can't rely on updateTransforms
437 | // to work here because the task is no longer in the list
438 | OverviewCard tv = getChildViewForIndex(removedTask);
439 | ViewHolder holder = mViewHolderMap.get(tv);
440 |
441 | // Notify the callback that we've removed the task and it can clean up after it
442 | mCb.onCardDismissed(removedTask);
443 |
444 | if (tv != null) {
445 | holder.setPosition(-1);
446 | mViewPool.returnObjectToPool(holder);
447 | }
448 |
449 | for (ViewHolder vh : mViewHolderMap.values())
450 | {
451 | if (vh.getPosition() > removedTask) {
452 | vh.setPosition(vh.getPosition() - 1);
453 | // No need to rebind, it's just an index change.
454 | }
455 | }
456 |
457 | // Get the stack scroll of the task to anchor to (since we are removing something, the front
458 | // most task will be our anchor task)
459 | int anchorPosition = -1;
460 | float prevAnchorTaskScroll = 0;
461 | boolean pullStackForward = stack.getNumberOfItems() > 0;
462 | if (pullStackForward) {
463 | anchorPosition = stack.getNumberOfItems() - 1;
464 | prevAnchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorPosition);
465 | }
466 |
467 | // Update the min/max scroll and animate other task views into their new positions
468 | updateMinMaxScroll(true);
469 |
470 | // Offset the stack by as much as the anchor task would otherwise move back
471 | if (pullStackForward) {
472 | float anchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorPosition);
473 | mStackScroller.setStackScroll(mStackScroller.getStackScroll() + (anchorTaskScroll
474 | - prevAnchorTaskScroll));
475 | mStackScroller.boundScroll();
476 | }
477 |
478 | // Animate all the tasks into place
479 | requestSynchronizeStackViewsWithModel(200);
480 |
481 | // If there are no remaining tasks, then either unfilter the current stack, or just close
482 | // the activity if there are no filtered stacks
483 | if (mStack.getNumberOfItems() == 0) {
484 | mCb.onAllCardsDismissed();
485 | }
486 | }
487 |
488 | public void onCardDismissed(OverviewCard tv) {
489 |
490 | ViewHolder vh = mViewHolderMap.get(tv);
491 | int taskIndex = vh.getPosition();
492 | mStack.notifyDataSetRemoved(taskIndex);
493 | }
494 |
495 | @Override
496 | public ViewHolder createObject(Context context) {
497 | return mStack.createViewHolder(context, mConfig);
498 | }
499 |
500 | @Override
501 | public void prepareObjectToEnterPool(ViewHolder vh) {
502 |
503 | mViewHolderMap.remove(vh.getContainer());
504 | // Detach the view from the hierarchy
505 | detachViewFromParent(vh.getContainer());
506 |
507 | // Reset the view properties
508 | vh.getContainer().resetViewProperties();
509 | }
510 |
511 | @Override
512 | public void prepareObjectToLeavePool(ViewHolder vh, Integer position, boolean isNewView) {
513 | // Rebind the task and request that this task's data be filled into the TaskView
514 |
515 | mViewHolderMap.put(vh.getContainer(), vh);
516 | vh.setPosition(position);
517 | mStack.bindViewHolder(vh, position);
518 | OverviewCard container = vh.getContainer();
519 |
520 | // Find the index where this task should be placed in the stack
521 | int insertIndex = -1;
522 | int taskIndex = position;
523 | if (taskIndex != -1) {
524 | int childCount = getChildCount();
525 | for (int i = 0; i < childCount; i++) {
526 | OverviewCard insertTV = (OverviewCard)getChildAt(i);
527 | ViewHolder holder = mViewHolderMap.get(insertTV);
528 | if (taskIndex < holder.getPosition()) {
529 | insertIndex = i;
530 | break;
531 | }
532 | }
533 | }
534 |
535 | // Add/attach the view to the hierarchy
536 | if (isNewView) {
537 | addView(container, insertIndex);
538 |
539 | // Set the callbacks and listeners for this new view
540 | container.setTouchEnabled(true);
541 | } else {
542 | attachViewToParent(container, insertIndex, container.getLayoutParams());
543 | }
544 | }
545 |
546 | @Override
547 | public boolean hasPreferredData(ViewHolder vh, Integer preferredData) {
548 | return (vh.getPosition() == preferredData);
549 | }
550 |
551 | /**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
552 | @Override
553 | public void onScrollChanged(float p) {
554 | requestSynchronizeStackViewsWithModel();
555 | if (Build.VERSION.SDK_INT >= 16) {
556 | postInvalidateOnAnimation();
557 | }
558 | else
559 | {
560 | new Handler(Looper.getMainLooper()).post(new Runnable() {
561 | @Override
562 | public void run() {
563 | invalidate();
564 | }
565 | });
566 | }
567 | }
568 | }
569 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/views/OverviewStackViewLayoutAlgorithm.java:
--------------------------------------------------------------------------------
1 | package com.wirelesspienetwork.overview.views;
2 |
3 | import android.graphics.Rect;
4 | import android.util.Log;
5 |
6 | import com.wirelesspienetwork.overview.misc.OverviewConfiguration;
7 | import com.wirelesspienetwork.overview.misc.Utilities;
8 |
9 | import java.util.HashMap;
10 |
11 | public class OverviewStackViewLayoutAlgorithm {
12 |
13 | //最小卡片的显示比率
14 | static final float StackPeekMinScale = 0.8f; // The min scale of the last card in the peek area
15 |
16 | OverviewConfiguration mConfig;
17 |
18 | // The various rects that define the stack view
19 | Rect mViewRect = new Rect();
20 | Rect mStackVisibleRect = new Rect();
21 | Rect mStackRect = new Rect();
22 | Rect mTaskRect = new Rect();
23 |
24 | // The min/max scroll progress
25 | float mMinScrollP;
26 | float mMaxScrollP;
27 | float mInitialScrollP;
28 | int mWithinAffiliationOffset;
29 | int mBetweenAffiliationOffset;
30 |
31 | //存放个个cardview的比率
32 | HashMap mTaskProgressMap = new HashMap();
33 |
34 | // Log function
35 | static final float XScale = 1.75f; // The large the XScale, the longer the flat area of the curve
36 | static final float LogBase = 3000;
37 | static final int PrecisionSteps = 250;
38 |
39 | //xp[PrecisionSteps] 这个是每段x的平均渐进累加值,与弧度渐进值的靠近比率。也就是说会越来越快
40 | static float[] xp;
41 |
42 | //当前阶段总弧度,所占总体的百分比() (0->1)
43 | static float[] px;
44 |
45 | public OverviewStackViewLayoutAlgorithm(OverviewConfiguration config) {
46 | mConfig = config;
47 |
48 | // Precompute the path
49 | initializeCurve();
50 | }
51 |
52 | /** Computes the stack and task rects */
53 | public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
54 | // Compute the stack rects
55 | mViewRect.set(0, 0, windowWidth, windowHeight);
56 | mStackRect.set(taskStackBounds);
57 | mStackVisibleRect.set(taskStackBounds);
58 | mStackVisibleRect.bottom = mViewRect.bottom;
59 |
60 | int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
61 | int heightPadding = mConfig.taskStackTopPaddingPx;
62 | mStackRect.inset(widthPadding, heightPadding);
63 |
64 | // Compute the task rect
65 | int width = mStackRect.width();
66 | int height = mStackRect.height();
67 | int left = mStackRect.left + (mStackRect.width() - width) / 2;
68 | mTaskRect.set(left, mStackRect.top,
69 | left + width, mStackRect.top + height);
70 |
71 | // Update the affiliation offsets
72 | //这里设置cardview之间的各种参数
73 | float visibleTaskPct = 0.5f;
74 | mWithinAffiliationOffset = 0;
75 | mBetweenAffiliationOffset = (int) (visibleTaskPct * mTaskRect.height());
76 | }
77 |
78 | /** Computes the minimum and maximum scroll progress values. This method may be called before
79 | * the RecentsConfiguration is set, so we need to pass in the alt-tab state. */
80 | void computeMinMaxScroll(int itemCount) {
81 | // Clear the progress map
82 | mTaskProgressMap.clear();
83 |
84 | // Return early if we have no tasks
85 | if (itemCount < 1) {
86 | mMinScrollP = mMaxScrollP = 0;
87 | return;
88 | }
89 |
90 | // Note that we should account for the scale difference of the offsets at the screen bottom
91 | int taskHeight = mTaskRect.height();
92 | float pAtBottomOfStackRect = screenYToCurveProgress(mStackVisibleRect.bottom);
93 | float pWithinAffiliateTop = screenYToCurveProgress(mStackVisibleRect.bottom -
94 | mWithinAffiliationOffset);
95 | float scale = curveProgressToScale(pWithinAffiliateTop);
96 | int scaleYOffset = (int) (((1f - scale) * taskHeight) / 2);
97 | pWithinAffiliateTop = screenYToCurveProgress(mStackVisibleRect.bottom -
98 | mWithinAffiliationOffset + scaleYOffset);
99 | float pWithinAffiliateOffset = pAtBottomOfStackRect - pWithinAffiliateTop;
100 | float pBetweenAffiliateOffset = pAtBottomOfStackRect -
101 | screenYToCurveProgress(mStackVisibleRect.bottom - mBetweenAffiliationOffset);
102 | float pTaskHeightOffset = pAtBottomOfStackRect -
103 | screenYToCurveProgress(mStackVisibleRect.bottom - taskHeight);
104 | float pNavBarOffset = pAtBottomOfStackRect -
105 | screenYToCurveProgress(mStackVisibleRect.bottom - (mStackVisibleRect.bottom - mStackRect.bottom));
106 |
107 | // Update the task offsets
108 | float pAtBackMostCardTop = 0.5f;
109 | float pAtFrontMostCardTop = pAtBackMostCardTop;
110 | for (int i = 0; i < itemCount; i++) {
111 | mTaskProgressMap.put(i, pAtFrontMostCardTop);
112 |
113 | if (i < (itemCount - 1)) {
114 | // Increment the peek height
115 | float pPeek = pBetweenAffiliateOffset;
116 | pAtFrontMostCardTop += pPeek;
117 | }
118 | }
119 |
120 | mMaxScrollP = pAtFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
121 | mMinScrollP = itemCount == 1 ? Math.max(mMaxScrollP, 0f) : 0f;
122 | mInitialScrollP = Math.max(0, pAtFrontMostCardTop);
123 | }
124 |
125 | /** Update/get the transform */
126 | /**
127 | * 由初始化的mTaskProgressMap来构建 view各自OverviewCardTransform 的绘制
128 | * @param position
129 | * @param stackScroll
130 | * @param transformOut
131 | * @param prevTransform
132 | * @return
133 | */
134 | public OverviewCardTransform getStackTransform(int position, float stackScroll, OverviewCardTransform transformOut,
135 | OverviewCardTransform prevTransform) {
136 | // Return early if we have an invalid index
137 | if (!mTaskProgressMap.containsKey(position)) {
138 | transformOut.reset();
139 | return transformOut;
140 | }
141 | return getStackTransform(mTaskProgressMap.get(position), stackScroll, transformOut, prevTransform);
142 | }
143 |
144 | /** Update/get the transform */
145 | public OverviewCardTransform getStackTransform(float taskProgress, float stackScroll, OverviewCardTransform transformOut, OverviewCardTransform prevTransform) {
146 | float pTaskRelative = taskProgress - stackScroll;
147 | float pBounded = Math.max(0, Math.min(pTaskRelative, 1f));
148 | // 大于1就说明已经扩大到屏幕外了 If the task top is outside of the bounds below the screen, then immediately reset it
149 | if (pTaskRelative > 1f) {
150 | transformOut.reset();
151 | transformOut.rect.set(mTaskRect);
152 | return transformOut;
153 | }
154 | // The check for the top is trickier, since we want to show the next task if it is at all
155 | // visible, even if p < 0.
156 | if (pTaskRelative < 0f) {
157 | if (prevTransform != null && Float.compare(prevTransform.p, 0f) <= 0) {
158 | transformOut.reset();
159 | transformOut.rect.set(mTaskRect);
160 | return transformOut;
161 | }
162 | }
163 | float scale = curveProgressToScale(pBounded);
164 | int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
165 | int minZ = mConfig.taskViewTranslationZMinPx;
166 | int maxZ = mConfig.taskViewTranslationZMaxPx;
167 | transformOut.scale = scale;
168 | transformOut.translationY = curveProgressToScreenY(pBounded) - mStackVisibleRect.top -
169 | scaleYOffset;
170 | transformOut.translationZ = Math.max(minZ, minZ + (pBounded * (maxZ - minZ)));
171 | transformOut.rect.set(mTaskRect);
172 | transformOut.rect.offset(0, transformOut.translationY);
173 | Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
174 | transformOut.visible = true;
175 | transformOut.p = pTaskRelative;
176 | return transformOut;
177 | }
178 |
179 | /**
180 | * Returns the scroll to such task top = 1f;
181 | */
182 | float getStackScrollForTask(int index) {
183 | return mTaskProgressMap.get(index);
184 | }
185 |
186 | /** Initializes the curve. */
187 | public static void initializeCurve() {
188 | if (xp != null && px != null) return;
189 | xp = new float[PrecisionSteps + 1];
190 | px = new float[PrecisionSteps + 1];
191 |
192 | // Approximate f(x)
193 | float[] fx = new float[PrecisionSteps + 1];
194 | float step = 1f / PrecisionSteps;
195 | float x = 0;
196 |
197 | for (int xStep = 0; xStep <= PrecisionSteps; xStep++) {
198 |
199 | //1-(3000^(1-1.75*x))/3000 就是这样一个先快后面的函数步骤 (0->1)
200 | //fx[xStep]:每个阶段的y值 (0->1)
201 |
202 | fx[xStep] = logFunc(x);
203 | Log.e("fx[xStep]: ","fx[xStep] : "+xStep +" "+fx[xStep]);
204 | x += step;
205 | }
206 |
207 | // Calculate the arc length for x:1->0
208 | float pLength = 0;
209 | float[] dx = new float[PrecisionSteps + 1];
210 | dx[0] = 0;
211 |
212 | //dx[xStep]:每个阶段差值间的距离,即直线弧度
213 | //pLength: 总弧度 (0->1.783)
214 |
215 | for (int xStep = 1; xStep < PrecisionSteps; xStep++) {
216 | dx[xStep] = (float) Math.sqrt(Math.pow(fx[xStep] - fx[xStep - 1], 2) + Math.pow(step, 2));
217 | pLength += dx[xStep];
218 | }
219 |
220 | // Approximate p(x), a function of cumulative progress with x, normalized to 0..1
221 | float p = 0;
222 | px[0] = 0f;
223 | px[PrecisionSteps] = 1f;
224 |
225 | for (int xStep = 1; xStep <= PrecisionSteps; xStep++) {
226 |
227 | //px[xStep]: 当前阶段总弧度,所占总体的百分比() (0->1)
228 |
229 | p += Math.abs(dx[xStep] / pLength);
230 | px[xStep] = p;
231 | }
232 | // Given p(x), calculate the inverse function x(p). This assumes that x(p) is also a valid
233 | // function.
234 |
235 | int xStep = 0;
236 | p = 0;
237 | xp[0] = 0f;
238 | xp[PrecisionSteps] = 1f;
239 |
240 | //xp[PrecisionSteps] 这个是每段x的平均渐进累加值,与弧度渐进值的靠近比率。也就是说会越来越快 (0->1)
241 |
242 | for (int pStep = 0; pStep < PrecisionSteps; pStep++) {
243 | // Walk forward in px and find the x where px <= p && p < px+1
244 | while (xStep < PrecisionSteps) {
245 | if (px[xStep] > p) break;
246 | xStep++;
247 | }
248 | // Now, px[xStep-1] <= p < px[xStep]
249 | if (xStep == 0) {
250 | xp[pStep] = 0;
251 | } else {
252 | // Find x such that proportionally, x is correct
253 | float fraction = (p - px[xStep - 1]) / (px[xStep] - px[xStep - 1]);
254 | x = (xStep - 1 + fraction) * step;
255 | xp[pStep] = x;
256 | }
257 | p += step;
258 | }
259 |
260 | }
261 |
262 | /** Reverses and scales out x. */
263 | static float reverse(float x) {
264 | return (-x * XScale) + 1;
265 | }
266 |
267 | /** The log function describing the curve. */
268 | static float logFunc(float x) {
269 | return 1f - (float) (Math.pow(LogBase, reverse(x))) / (LogBase);
270 | }
271 |
272 | /** Converts from the progress along the curve to a screen coordinate. */
273 | int curveProgressToScreenY(float p) {
274 | if (p < 0 || p > 1) return mStackVisibleRect.top + (int) (p * mStackVisibleRect.height());
275 | float pIndex = p * PrecisionSteps;
276 | int pFloorIndex = (int) Math.floor(pIndex);
277 | int pCeilIndex = (int) Math.ceil(pIndex);
278 | float xFraction = 0;
279 | if (pFloorIndex < PrecisionSteps && (pCeilIndex != pFloorIndex)) {
280 | float pFraction = (pIndex - pFloorIndex) / (pCeilIndex - pFloorIndex);
281 | xFraction = (xp[pCeilIndex] - xp[pFloorIndex]) * pFraction;
282 | }
283 | float x = xp[pFloorIndex] + xFraction;
284 | return mStackVisibleRect.top + (int) (x * mStackVisibleRect.height());
285 | }
286 |
287 | /** Converts from the progress along the curve to a scale. */
288 | /**
289 | * 扩大的范围线性计算
290 | * @param p 当前比率
291 | * @return 经由缩小值计算后的比率
292 | */
293 | float curveProgressToScale(float p) {
294 | if (p < 0) return StackPeekMinScale;
295 | if (p > 1) return 1f;
296 | float scaleRange = (1f - StackPeekMinScale);
297 | float scale = StackPeekMinScale + (p * scaleRange);
298 | return scale;
299 | }
300 |
301 |
302 | /**
303 | * 纵坐标装换成曲线走势
304 | * Converts from a screen coordinate to the progress along the curve.
305 | *
306 | * @param screenY 需要转换的高度
307 | * @return 输入高度实际占曲线的百分比
308 | */
309 | float screenYToCurveProgress(int screenY) {
310 | float x = (float) (screenY - mStackVisibleRect.top) / mStackVisibleRect.height();
311 | if (x < 0 || x > 1) return x;
312 | float xIndex = x * PrecisionSteps;
313 | int xFloorIndex = (int) Math.floor(xIndex);
314 | int xCeilIndex = (int) Math.ceil(xIndex);
315 | float pFraction = 0;
316 | if (xFloorIndex < PrecisionSteps && (xCeilIndex != xFloorIndex)) {
317 |
318 | //精确到小数部分值计算
319 | float xFraction = (xIndex - xFloorIndex) / (xCeilIndex - xFloorIndex);
320 | pFraction = (px[xCeilIndex] - px[xFloorIndex]) * xFraction;
321 | }
322 |
323 | //转换后的弧度比 和 其小数弧度 的和
324 | return px[xFloorIndex] + pFraction;
325 | }
326 | }
327 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/views/OverviewStackViewScroller.java:
--------------------------------------------------------------------------------
1 | package com.wirelesspienetwork.overview.views;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ObjectAnimator;
6 | import android.animation.ValueAnimator;
7 | import android.content.Context;
8 | import android.widget.OverScroller;
9 |
10 | import com.wirelesspienetwork.overview.misc.OverviewConfiguration;
11 |
12 | /* The scrolling logic for a TaskStackView */
13 | public class OverviewStackViewScroller {
14 | public interface Callbacks {
15 | public void onScrollChanged(float p);
16 | }
17 |
18 | OverviewConfiguration mConfig;
19 | OverviewStackViewLayoutAlgorithm mLayoutAlgorithm;
20 | Callbacks mCb;
21 |
22 | float mStackScrollP;
23 |
24 | OverScroller mScroller;
25 | ObjectAnimator mScrollAnimator;
26 |
27 | public OverviewStackViewScroller(Context context, OverviewConfiguration config, OverviewStackViewLayoutAlgorithm layoutAlgorithm) {
28 | mConfig = config;
29 | mScroller = new OverScroller(context);
30 | mLayoutAlgorithm = layoutAlgorithm;
31 | setStackScroll(getStackScroll());
32 | }
33 |
34 | /** Sets the callbacks */
35 | void setCallbacks(Callbacks cb) {
36 | mCb = cb;
37 | }
38 |
39 | /** Gets the current stack scroll */
40 | public float getStackScroll() {
41 | return mStackScrollP;
42 | }
43 |
44 | /** Sets the current stack scroll */
45 | public void setStackScroll(float s) {
46 | mStackScrollP = s;
47 | if (mCb != null) {
48 | mCb.onScrollChanged(mStackScrollP);
49 | }
50 | }
51 |
52 | /** Sets the current stack scroll without calling the callback. */
53 | void setStackScrollRaw(float s) {
54 | mStackScrollP = s;
55 | }
56 |
57 | /** Sets the current stack scroll to the initial state when you first enter recents */
58 | public void setStackScrollToInitialState() {
59 | setStackScroll(getBoundedStackScroll(mLayoutAlgorithm.mInitialScrollP));
60 | }
61 |
62 | /** Bounds the current scroll if necessary */
63 | public boolean boundScroll() {
64 | float curScroll = getStackScroll();
65 | float newScroll = getBoundedStackScroll(curScroll);
66 | if (Float.compare(newScroll, curScroll) != 0) {
67 | setStackScroll(newScroll);
68 | return true;
69 | }
70 | return false;
71 | }
72 | /** Bounds the current scroll if necessary, but does not synchronize the stack view with the model. */
73 | public boolean boundScrollRaw() {
74 | float curScroll = getStackScroll();
75 | float newScroll = getBoundedStackScroll(curScroll);
76 | if (Float.compare(newScroll, curScroll) != 0) {
77 | setStackScrollRaw(newScroll);
78 | return true;
79 | }
80 | return false;
81 | }
82 |
83 | /** Returns the bounded stack scroll */
84 | float getBoundedStackScroll(float scroll) {
85 | return Math.max(mLayoutAlgorithm.mMinScrollP, Math.min(mLayoutAlgorithm.mMaxScrollP, scroll));
86 | }
87 |
88 | /** Returns the amount that the aboslute value of how much the scroll is out of bounds. */
89 | float getScrollAmountOutOfBounds(float scroll) {
90 | if (scroll < mLayoutAlgorithm.mMinScrollP) {
91 | return Math.abs(scroll - mLayoutAlgorithm.mMinScrollP);
92 | } else if (scroll > mLayoutAlgorithm.mMaxScrollP) {
93 | return Math.abs(scroll - mLayoutAlgorithm.mMaxScrollP);
94 | }
95 | return 0f;
96 | }
97 |
98 | /** Returns whether the specified scroll is out of bounds */
99 | boolean isScrollOutOfBounds() {
100 | return Float.compare(getScrollAmountOutOfBounds(mStackScrollP), 0f) != 0;
101 | }
102 |
103 | /** Animates the stack scroll into bounds */
104 | ObjectAnimator animateBoundScroll() {
105 | float curScroll = getStackScroll();
106 | float newScroll = getBoundedStackScroll(curScroll);
107 | if (Float.compare(newScroll, curScroll) != 0) {
108 | // Start a new scroll animation
109 | animateScroll(curScroll, newScroll, null);
110 | }
111 | return mScrollAnimator;
112 | }
113 |
114 | /** Animates the stack scroll */
115 | void animateScroll(float curScroll, float newScroll, final Runnable postRunnable) {
116 | // Abort any current animations
117 | stopScroller();
118 | stopBoundScrollAnimation();
119 |
120 | mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll);
121 | mScrollAnimator.setDuration(mConfig.taskStackScrollDuration);
122 | mScrollAnimator.setInterpolator(mConfig.linearOutSlowInInterpolator);
123 | mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
124 | @Override
125 | public void onAnimationUpdate(ValueAnimator animation) {
126 | setStackScroll((Float) animation.getAnimatedValue());
127 | }
128 | });
129 | mScrollAnimator.addListener(new AnimatorListenerAdapter() {
130 | @Override
131 | public void onAnimationEnd(Animator animation) {
132 | if (postRunnable != null) {
133 | postRunnable.run();
134 | }
135 | mScrollAnimator.removeAllListeners();
136 | }
137 | });
138 | mScrollAnimator.start();
139 | }
140 |
141 | /** Aborts any current stack scrolls */
142 | void stopBoundScrollAnimation() {
143 | if (mScrollAnimator != null) {
144 | mScrollAnimator.removeAllListeners();
145 | mScrollAnimator.cancel();
146 | }
147 | }
148 |
149 | /**** OverScroller ****/
150 |
151 | int progressToScrollRange(float p) {
152 | return (int) (p * mLayoutAlgorithm.mStackVisibleRect.height());
153 | }
154 |
155 | float scrollRangeToProgress(int s) {
156 | return (float) s / mLayoutAlgorithm.mStackVisibleRect.height();
157 | }
158 |
159 | /** Called from the view draw, computes the next scroll. */
160 | boolean computeScroll() {
161 | if (mScroller.computeScrollOffset()) {
162 | float scroll = scrollRangeToProgress(mScroller.getCurrY());
163 | setStackScrollRaw(scroll);
164 | if (mCb != null) {
165 | mCb.onScrollChanged(scroll);
166 | }
167 | return true;
168 | }
169 | return false;
170 | }
171 |
172 | /** Returns whether the overscroller is scrolling. */
173 | boolean isScrolling() {
174 | return !mScroller.isFinished();
175 | }
176 |
177 | /** Stops the scroller and any current fling. */
178 | void stopScroller() {
179 | if (!mScroller.isFinished()) {
180 | mScroller.abortAnimation();
181 | }
182 | }
183 | }
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/views/OverviewStackViewTouchHandler.java:
--------------------------------------------------------------------------------
1 | package com.wirelesspienetwork.overview.views;
2 |
3 | import android.content.Context;
4 | import android.view.MotionEvent;
5 | import android.view.VelocityTracker;
6 | import android.view.View;
7 | import android.view.ViewConfiguration;
8 | import android.view.ViewParent;
9 | import com.wirelesspienetwork.overview.misc.OverviewConfiguration;
10 |
11 | class OverviewStackViewTouchHandler implements SwipeHelper.Callback {
12 | static int INACTIVE_POINTER_ID = -1;
13 |
14 | static final int TaskStackOverscrollRange = 150;
15 |
16 | OverviewConfiguration mConfig;
17 | OverviewStackView mSv;
18 | OverviewStackViewScroller mScroller;
19 | VelocityTracker mVelocityTracker;
20 |
21 | boolean mIsScrolling;
22 |
23 | float mInitialP;
24 | float mLastP;
25 | float mTotalPMotion;
26 | int mInitialMotionX, mInitialMotionY;
27 | int mLastMotionX, mLastMotionY;
28 | int mActivePointerId = INACTIVE_POINTER_ID;
29 | OverviewCard mActiveTaskView = null;
30 |
31 | int mMinimumVelocity;
32 | int mMaximumVelocity;
33 | // The scroll touch slop is used to calculate when we start scrolling
34 | int mScrollTouchSlop;
35 | // The page touch slop is used to calculate when we start swiping
36 | float mPagingTouchSlop;
37 |
38 | SwipeHelper mSwipeHelper;
39 | boolean mInterceptedBySwipeHelper;
40 |
41 | public OverviewStackViewTouchHandler(Context context, OverviewStackView sv,
42 | OverviewConfiguration config, OverviewStackViewScroller scroller) {
43 | ViewConfiguration configuration = ViewConfiguration.get(context);
44 | mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
45 | mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
46 | mScrollTouchSlop = configuration.getScaledTouchSlop();
47 | mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
48 | mSv = sv;
49 | mScroller = scroller;
50 | mConfig = config;
51 |
52 | float densityScale = context.getResources().getDisplayMetrics().density;
53 | mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, mPagingTouchSlop, mConfig);
54 | }
55 |
56 | /** Velocity tracker helpers */
57 | void initOrResetVelocityTracker() {
58 | if (mVelocityTracker == null) {
59 | mVelocityTracker = VelocityTracker.obtain();
60 | } else {
61 | mVelocityTracker.clear();
62 | }
63 | }
64 | void initVelocityTrackerIfNotExists() {
65 | if (mVelocityTracker == null) {
66 | mVelocityTracker = VelocityTracker.obtain();
67 | }
68 | }
69 | void recycleVelocityTracker() {
70 | if (mVelocityTracker != null) {
71 | mVelocityTracker.recycle();
72 | mVelocityTracker = null;
73 | }
74 | }
75 |
76 | /** Returns the view at the specified coordinates */
77 | OverviewCard findViewAtPoint(int x, int y) {
78 | int childCount = mSv.getChildCount();
79 | for (int i = childCount - 1; i >= 0; i--) {
80 | OverviewCard tv = (OverviewCard) mSv.getChildAt(i);
81 | if (tv.getVisibility() == View.VISIBLE) {
82 | if (mSv.isTransformedTouchPointInView(x, y, tv)) {
83 | return tv;
84 | }
85 | }
86 | }
87 | return null;
88 | }
89 |
90 | /** Constructs a simulated motion event for the current stack scroll. */
91 | MotionEvent createMotionEventForStackScroll(MotionEvent ev) {
92 | MotionEvent pev = MotionEvent.obtainNoHistory(ev);
93 | pev.setLocation(0, mScroller.progressToScrollRange(mScroller.getStackScroll()));
94 | return pev;
95 | }
96 |
97 | /** Touch preprocessing for handling below */
98 | public boolean onInterceptTouchEvent(MotionEvent ev) {
99 | // Return early if we have no children
100 | boolean hasChildren = (mSv.getChildCount() > 0);
101 | if (!hasChildren) {
102 | return false;
103 | }
104 |
105 | // Pass through to swipe helper if we are swiping
106 | mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
107 | if (mInterceptedBySwipeHelper) {
108 | return true;
109 | }
110 |
111 | boolean wasScrolling = mScroller.isScrolling() ||
112 | (mScroller.mScrollAnimator != null && mScroller.mScrollAnimator.isRunning());
113 | int action = ev.getAction();
114 | switch (action & MotionEvent.ACTION_MASK) {
115 | case MotionEvent.ACTION_DOWN: {
116 | // Save the touch down info
117 | mInitialMotionX = mLastMotionX = (int) ev.getX();
118 | mInitialMotionY = mLastMotionY = (int) ev.getY();
119 | mInitialP = mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
120 | mActivePointerId = ev.getPointerId(0);
121 | mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
122 | // Stop the current scroll if it is still flinging
123 | mScroller.stopScroller();
124 | mScroller.stopBoundScrollAnimation();
125 | // Initialize the velocity tracker
126 | initOrResetVelocityTracker();
127 | mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
128 | // Check if the scroller is finished yet
129 | mIsScrolling = mScroller.isScrolling();
130 | break;
131 | }
132 | case MotionEvent.ACTION_MOVE: {
133 | if (mActivePointerId == INACTIVE_POINTER_ID) break;
134 |
135 | int activePointerIndex = ev.findPointerIndex(mActivePointerId);
136 | int y = (int) ev.getY(activePointerIndex);
137 | int x = (int) ev.getX(activePointerIndex);
138 | if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
139 | // Save the touch move info
140 | mIsScrolling = true;
141 | // Initialize the velocity tracker if necessary
142 | initVelocityTrackerIfNotExists();
143 | mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
144 | // Disallow parents from intercepting touch events
145 | final ViewParent parent = mSv.getParent();
146 | if (parent != null) {
147 | parent.requestDisallowInterceptTouchEvent(true);
148 | }
149 | }
150 |
151 | mLastMotionX = x;
152 | mLastMotionY = y;
153 | mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
154 | break;
155 | }
156 | case MotionEvent.ACTION_CANCEL:
157 | case MotionEvent.ACTION_UP: {
158 | // Animate the scroll back if we've cancelled
159 | mScroller.animateBoundScroll();
160 | // Reset the drag state and the velocity tracker
161 | mIsScrolling = false;
162 | mActivePointerId = INACTIVE_POINTER_ID;
163 | mActiveTaskView = null;
164 | mTotalPMotion = 0;
165 | recycleVelocityTracker();
166 | break;
167 | }
168 | }
169 |
170 | return wasScrolling || mIsScrolling;
171 | }
172 |
173 | /** Handles touch events once we have intercepted them */
174 | public boolean onTouchEvent(MotionEvent ev) {
175 |
176 | // Short circuit if we have no children
177 | boolean hasChildren = (mSv.getChildCount() > 0);
178 | if (!hasChildren) {
179 | return false;
180 | }
181 |
182 | // Pass through to swipe helper if we are swiping
183 | if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
184 | return true;
185 | }
186 |
187 | // Update the velocity tracker
188 | initVelocityTrackerIfNotExists();
189 |
190 | int action = ev.getAction();
191 | switch (action & MotionEvent.ACTION_MASK) {
192 | case MotionEvent.ACTION_DOWN: {
193 | // Save the touch down info
194 | mInitialMotionX = mLastMotionX = (int) ev.getX();
195 | mInitialMotionY = mLastMotionY = (int) ev.getY();
196 | mInitialP = mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
197 | mActivePointerId = ev.getPointerId(0);
198 | mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
199 | // Stop the current scroll if it is still flinging
200 | mScroller.stopScroller();
201 | mScroller.stopBoundScrollAnimation();
202 | // Initialize the velocity tracker
203 | initOrResetVelocityTracker();
204 | mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
205 | // Disallow parents from intercepting touch events
206 | final ViewParent parent = mSv.getParent();
207 | if (parent != null) {
208 | parent.requestDisallowInterceptTouchEvent(true);
209 | }
210 | break;
211 | }
212 | case MotionEvent.ACTION_POINTER_DOWN: {
213 | final int index = ev.getActionIndex();
214 | mActivePointerId = ev.getPointerId(index);
215 | mLastMotionX = (int) ev.getX(index);
216 | mLastMotionY = (int) ev.getY(index);
217 | mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
218 | break;
219 | }
220 | case MotionEvent.ACTION_MOVE: {
221 | if (mActivePointerId == INACTIVE_POINTER_ID) break;
222 |
223 | int activePointerIndex = ev.findPointerIndex(mActivePointerId);
224 | int x = (int) ev.getX(activePointerIndex);
225 | int y = (int) ev.getY(activePointerIndex);
226 | int yTotal = Math.abs(y - mInitialMotionY);
227 | float curP = mSv.mLayoutAlgorithm.screenYToCurveProgress(y);
228 | float deltaP = mLastP - curP;
229 | if (!mIsScrolling) {
230 | if (yTotal > mScrollTouchSlop) {
231 | mIsScrolling = true;
232 | // Initialize the velocity tracker
233 | initOrResetVelocityTracker();
234 | mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
235 | // Disallow parents from intercepting touch events
236 | final ViewParent parent = mSv.getParent();
237 | if (parent != null) {
238 | parent.requestDisallowInterceptTouchEvent(true);
239 | }
240 | }
241 | }
242 | if (mIsScrolling) {
243 | float curStackScroll = mScroller.getStackScroll();
244 | float overScrollAmount = mScroller.getScrollAmountOutOfBounds(curStackScroll + deltaP);
245 | if (Float.compare(overScrollAmount, 0f) != 0) {
246 | // Bound the overscroll to a fixed amount, and inversely scale the y-movement
247 | // relative to how close we are to the max overscroll
248 | float maxOverScroll = mConfig.taskStackOverscrollPct;
249 | deltaP *= (1f - (Math.min(maxOverScroll, overScrollAmount)
250 | / maxOverScroll));
251 | }
252 | mScroller.setStackScroll(curStackScroll + deltaP);
253 | if (mScroller.isScrollOutOfBounds()) {
254 | mVelocityTracker.clear();
255 | } else {
256 | mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
257 | }
258 | }
259 | mLastMotionX = x;
260 | mLastMotionY = y;
261 | mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
262 | mTotalPMotion += Math.abs(deltaP);
263 | break;
264 | }
265 | case MotionEvent.ACTION_UP: {
266 | final VelocityTracker velocityTracker = mVelocityTracker;
267 | velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
268 | int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
269 | if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
270 | int overscrollRange = (int) (Math.min(1f,
271 | Math.abs((float) velocity / mMaximumVelocity)) *
272 | TaskStackOverscrollRange);
273 | // Fling scroll
274 | mScroller.mScroller.fling(0, mScroller.progressToScrollRange(mScroller.getStackScroll()),
275 | 0, velocity,
276 | 0, 0,
277 | mScroller.progressToScrollRange(mSv.mLayoutAlgorithm.mMinScrollP),
278 | mScroller.progressToScrollRange(mSv.mLayoutAlgorithm.mMaxScrollP),
279 | 0, overscrollRange);
280 | // Invalidate to kick off computeScroll
281 | mSv.invalidate();
282 | } else if (mScroller.isScrollOutOfBounds()) {
283 | // Animate the scroll back into bounds
284 | mScroller.animateBoundScroll();
285 | }
286 |
287 | mActivePointerId = INACTIVE_POINTER_ID;
288 | mIsScrolling = false;
289 | mTotalPMotion = 0;
290 | recycleVelocityTracker();
291 | break;
292 | }
293 | case MotionEvent.ACTION_POINTER_UP: {
294 | int pointerIndex = ev.getActionIndex();
295 | int pointerId = ev.getPointerId(pointerIndex);
296 | if (pointerId == mActivePointerId) {
297 | // Select a new active pointer id and reset the motion state
298 | final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
299 | mActivePointerId = ev.getPointerId(newPointerIndex);
300 | mLastMotionX = (int) ev.getX(newPointerIndex);
301 | mLastMotionY = (int) ev.getY(newPointerIndex);
302 | mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
303 | mVelocityTracker.clear();
304 | }
305 | break;
306 | }
307 | case MotionEvent.ACTION_CANCEL: {
308 | if (mScroller.isScrollOutOfBounds()) {
309 | // Animate the scroll back into bounds
310 | mScroller.animateBoundScroll();
311 | }
312 | mActivePointerId = INACTIVE_POINTER_ID;
313 | mIsScrolling = false;
314 | mTotalPMotion = 0;
315 | recycleVelocityTracker();
316 | break;
317 | }
318 | }
319 | return true;
320 | }
321 |
322 | /**** SwipeHelper Implementation ****/
323 |
324 | @Override
325 | public View getChildAtPosition(MotionEvent ev) {
326 | return findViewAtPoint((int) ev.getX(), (int) ev.getY());
327 | }
328 |
329 | @Override
330 | public boolean canChildBeDismissed(View v) {
331 | return true;
332 | }
333 |
334 | @Override
335 | public void onBeginDrag(View v) {
336 | OverviewCard tv = (OverviewCard) v;
337 | // Disallow touch events from this task view
338 | tv.setTouchEnabled(false);
339 | // Disallow parents from intercepting touch events
340 | final ViewParent parent = mSv.getParent();
341 | if (parent != null) {
342 | parent.requestDisallowInterceptTouchEvent(true);
343 | }
344 | }
345 |
346 | @Override
347 | public void onSwipeChanged(View v, float delta) {
348 | // Do nothing
349 | }
350 |
351 | @Override
352 | public void onChildDismissed(View v) {
353 | OverviewCard tv = (OverviewCard) v;
354 | // Re-enable touch events from this task view
355 | tv.setTouchEnabled(true);
356 | // Remove the task view from the stack
357 | mSv.onCardDismissed(tv);
358 | }
359 |
360 | @Override
361 | public void onSnapBackCompleted(View v) {
362 | OverviewCard tv = (OverviewCard) v;
363 | // Re-enable touch events from this task view
364 | tv.setTouchEnabled(true);
365 | }
366 |
367 | @Override
368 | public void onDragCancelled(View v) {
369 | // Do nothing
370 | }
371 | }
372 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/views/SwipeHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.wirelesspienetwork.overview.views;
18 |
19 | import android.animation.Animator;
20 | import android.animation.AnimatorListenerAdapter;
21 | import android.animation.ObjectAnimator;
22 | import android.animation.ValueAnimator;
23 | import android.animation.ValueAnimator.AnimatorUpdateListener;
24 | import android.util.DisplayMetrics;
25 | import android.view.MotionEvent;
26 | import android.view.VelocityTracker;
27 | import android.view.View;
28 | import android.view.animation.LinearInterpolator;
29 |
30 | import com.wirelesspienetwork.overview.misc.OverviewConfiguration;
31 |
32 | /**
33 | * This class facilitates swipe to dismiss. It defines an interface to be implemented by the
34 | * by the class hosting the views that need to swiped, and, using this interface, handles touch
35 | * events and translates / fades / animates the view as it is dismissed.
36 | */
37 | public class SwipeHelper {
38 | public static final int X = 0;
39 | public static final int Y = 1;
40 |
41 | private static LinearInterpolator sLinearInterpolator = new LinearInterpolator();
42 |
43 | private static final float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
44 | private static final int DEFAULT_ESCAPE_ANIMATION_DURATION = 75; // ms
45 | private static final int MAX_ESCAPE_ANIMATION_DURATION = 150; // ms
46 | private static final int MAX_DISMISS_VELOCITY = 2000; // dp/sec
47 | private static final int SNAP_ANIM_LEN = 250; // ms
48 |
49 | public static float ALPHA_FADE_START = 0.15f; // fraction of thumbnail width
50 | // where fade starts
51 | static final float ALPHA_FADE_END = 0.65f; // fraction of thumbnail width
52 | // beyond which alpha->0
53 | private float mMinAlpha = 0f;
54 |
55 | private float mPagingTouchSlop;
56 | Callback mCallback;
57 | private int mSwipeDirection;
58 | private VelocityTracker mVelocityTracker;
59 |
60 | private float mInitialTouchPos;
61 | private boolean mDragging;
62 |
63 | private View mCurrView;
64 | private float mDensityScale;
65 |
66 | public boolean mAllowSwipeTowardsStart = true;
67 | public boolean mAllowSwipeTowardsEnd = true;
68 |
69 | OverviewConfiguration mConfig;
70 |
71 | public SwipeHelper(int swipeDirection, Callback callback, float densityScale,
72 | float pagingTouchSlop, OverviewConfiguration config) {
73 | mCallback = callback;
74 | mSwipeDirection = swipeDirection;
75 | mVelocityTracker = VelocityTracker.obtain();
76 | mDensityScale = densityScale;
77 | mPagingTouchSlop = pagingTouchSlop;
78 | mConfig = config;
79 | }
80 |
81 | private float getPos(MotionEvent ev) {
82 | return mSwipeDirection == X ? ev.getX() : ev.getY();
83 | }
84 |
85 | private float getTranslation(View v) {
86 | return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY();
87 | }
88 |
89 | private float getVelocity(VelocityTracker vt) {
90 | return mSwipeDirection == X ? vt.getXVelocity() :
91 | vt.getYVelocity();
92 | }
93 |
94 | private ObjectAnimator createTranslationAnimation(View v, float newPos) {
95 | ObjectAnimator anim = ObjectAnimator.ofFloat(v,
96 | mSwipeDirection == X ? View.TRANSLATION_X : View.TRANSLATION_Y, newPos);
97 | return anim;
98 | }
99 |
100 | private float getPerpendicularVelocity(VelocityTracker vt) {
101 | return mSwipeDirection == X ? vt.getYVelocity() :
102 | vt.getXVelocity();
103 | }
104 |
105 | private void setTranslation(View v, float translate) {
106 | if (mSwipeDirection == X) {
107 | v.setTranslationX(translate);
108 | } else {
109 | v.setTranslationY(translate);
110 | }
111 | }
112 |
113 | private float getSize(View v) {
114 | final DisplayMetrics dm = v.getContext().getResources().getDisplayMetrics();
115 | return mSwipeDirection == X ? dm.widthPixels : dm.heightPixels;
116 | }
117 |
118 | public void setMinAlpha(float minAlpha) {
119 | mMinAlpha = minAlpha;
120 | }
121 |
122 | float getAlphaForOffset(View view) {
123 | float viewSize = getSize(view);
124 | final float fadeSize = ALPHA_FADE_END * viewSize;
125 | float result = 1.0f;
126 | float pos = getTranslation(view);
127 | if (pos >= viewSize * ALPHA_FADE_START) {
128 | result = 1.0f - (pos - viewSize * ALPHA_FADE_START) / fadeSize;
129 | } else if (pos < viewSize * -ALPHA_FADE_START) {
130 | result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
131 | }
132 | result = Math.min(result, 1.0f);
133 | result = Math.max(result, 0f);
134 | return Math.max(mMinAlpha, result);
135 | }
136 |
137 | public boolean onInterceptTouchEvent(MotionEvent ev) {
138 | final int action = ev.getAction();
139 |
140 | switch (action) {
141 | case MotionEvent.ACTION_DOWN:
142 | mDragging = false;
143 | mCurrView = mCallback.getChildAtPosition(ev);
144 | mVelocityTracker.clear();
145 | if (mCurrView != null) {
146 | mVelocityTracker.addMovement(ev);
147 | mInitialTouchPos = getPos(ev);
148 | }
149 | break;
150 | case MotionEvent.ACTION_MOVE:
151 | if (mCurrView != null) {
152 | mVelocityTracker.addMovement(ev);
153 | float pos = getPos(ev);
154 | float delta = pos - mInitialTouchPos;
155 | if (Math.abs(delta) > mPagingTouchSlop) {
156 | mCallback.onBeginDrag(mCurrView);
157 | mDragging = true;
158 | mInitialTouchPos = pos - getTranslation(mCurrView);
159 | }
160 | }
161 | break;
162 | case MotionEvent.ACTION_UP:
163 | case MotionEvent.ACTION_CANCEL:
164 | mDragging = false;
165 | mCurrView = null;
166 | break;
167 | }
168 | return mDragging;
169 | }
170 |
171 | /**
172 | * @param view The view to be dismissed
173 | * @param velocity The desired pixels/second speed at which the view should move
174 | */
175 | private void dismissChild(final View view, float velocity) {
176 | final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
177 | float newPos;
178 | if (velocity < 0
179 | || (velocity == 0 && getTranslation(view) < 0)
180 | // if we use the Menu to dismiss an item in landscape, animate up
181 | || (velocity == 0 && getTranslation(view) == 0 && mSwipeDirection == Y)) {
182 | newPos = -getSize(view);
183 | } else {
184 | newPos = getSize(view);
185 | }
186 | int duration = MAX_ESCAPE_ANIMATION_DURATION;
187 | if (velocity != 0) {
188 | duration = Math.min(duration,
189 | (int) (Math.abs(newPos - getTranslation(view)) *
190 | 1000f / Math.abs(velocity)));
191 | } else {
192 | duration = DEFAULT_ESCAPE_ANIMATION_DURATION;
193 | }
194 |
195 | ValueAnimator anim = createTranslationAnimation(view, newPos);
196 | anim.setInterpolator(sLinearInterpolator);
197 | anim.setDuration(duration);
198 | anim.addListener(new AnimatorListenerAdapter() {
199 | @Override
200 | public void onAnimationEnd(Animator animation) {
201 | mCallback.onChildDismissed(view);
202 | if (canAnimViewBeDismissed) {
203 | view.setAlpha(1.f);
204 | }
205 | }
206 | });
207 | anim.addUpdateListener(new AnimatorUpdateListener() {
208 | @Override
209 | public void onAnimationUpdate(ValueAnimator animation) {
210 | if ( canAnimViewBeDismissed) {
211 | view.setAlpha(getAlphaForOffset(view));
212 | }
213 | }
214 | });
215 | anim.start();
216 | }
217 |
218 | private void snapChild(final View view, float velocity) {
219 | final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
220 | ValueAnimator anim = createTranslationAnimation(view, 0);
221 | int duration = SNAP_ANIM_LEN;
222 | anim.setDuration(duration);
223 | anim.setInterpolator(mConfig.linearOutSlowInInterpolator);
224 | anim.addUpdateListener(new AnimatorUpdateListener() {
225 | @Override
226 | public void onAnimationUpdate(ValueAnimator animation) {
227 | if (canAnimViewBeDismissed) {
228 | view.setAlpha(getAlphaForOffset(view));
229 | }
230 | mCallback.onSwipeChanged(mCurrView, view.getTranslationX());
231 | }
232 | });
233 | anim.addListener(new AnimatorListenerAdapter() {
234 | @Override
235 | public void onAnimationEnd(Animator animation) {
236 | if (canAnimViewBeDismissed) {
237 | view.setAlpha(1.0f);
238 | }
239 | mCallback.onSnapBackCompleted(view);
240 | }
241 | });
242 | anim.start();
243 | }
244 |
245 | public boolean onTouchEvent(MotionEvent ev) {
246 | if (!mDragging) {
247 | if (!onInterceptTouchEvent(ev)) {
248 | return mCurrView != null;
249 | }
250 | }
251 |
252 | mVelocityTracker.addMovement(ev);
253 | final int action = ev.getAction();
254 | switch (action) {
255 | case MotionEvent.ACTION_OUTSIDE:
256 | case MotionEvent.ACTION_MOVE:
257 | if (mCurrView != null) {
258 | float delta = getPos(ev) - mInitialTouchPos;
259 | setSwipeAmount(delta);
260 | mCallback.onSwipeChanged(mCurrView, delta);
261 | }
262 | break;
263 | case MotionEvent.ACTION_UP:
264 | case MotionEvent.ACTION_CANCEL:
265 | if (mCurrView != null) {
266 | endSwipe(mVelocityTracker);
267 | }
268 | break;
269 | }
270 | return true;
271 | }
272 |
273 | private void setSwipeAmount(float amount) {
274 | // don't let items that can't be dismissed be dragged more than
275 | // maxScrollDistance
276 | if (!isValidSwipeDirection(amount)) {
277 | float size = getSize(mCurrView);
278 | float maxScrollDistance = 0.15f * size;
279 | if (Math.abs(amount) >= size) {
280 | amount = amount > 0 ? maxScrollDistance : -maxScrollDistance;
281 | } else {
282 | amount = maxScrollDistance * (float) Math.sin((amount/size)*(Math.PI/2));
283 | }
284 | }
285 | setTranslation(mCurrView, amount);
286 | float alpha = getAlphaForOffset(mCurrView);
287 | mCurrView.setAlpha(alpha);
288 | }
289 |
290 | private boolean isValidSwipeDirection(float amount) {
291 | if (mSwipeDirection == X) {
292 | return (amount <= 0) ? mAllowSwipeTowardsStart : mAllowSwipeTowardsEnd;
293 | }
294 |
295 | // Vertical swipes are always valid.
296 | return true;
297 | }
298 |
299 | private void endSwipe(VelocityTracker velocityTracker) {
300 | float maxVelocity = MAX_DISMISS_VELOCITY * mDensityScale;
301 | velocityTracker.computeCurrentVelocity(1000 /* px/sec */, maxVelocity);
302 | float velocity = getVelocity(velocityTracker);
303 | float perpendicularVelocity = getPerpendicularVelocity(velocityTracker);
304 | float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
305 | float translation = getTranslation(mCurrView);
306 | // Decide whether to dismiss the current view
307 | boolean childSwipedFarEnough = Math.abs(translation) > 0.6 * getSize(mCurrView);
308 | boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
309 | (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
310 | (velocity > 0) == (translation > 0);
311 |
312 | boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
313 | && isValidSwipeDirection(translation)
314 | && (childSwipedFastEnough || childSwipedFarEnough);
315 |
316 | if (dismissChild) {
317 | // flingadingy
318 | dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
319 | } else {
320 | // snappity
321 | mCallback.onDragCancelled(mCurrView);
322 | snapChild(mCurrView, velocity);
323 | }
324 | }
325 |
326 | public interface Callback {
327 | View getChildAtPosition(MotionEvent ev);
328 |
329 | boolean canChildBeDismissed(View v);
330 |
331 | void onBeginDrag(View v);
332 |
333 | void onSwipeChanged(View v, float delta);
334 |
335 | void onChildDismissed(View v);
336 |
337 | void onSnapBackCompleted(View v);
338 |
339 | void onDragCancelled(View v);
340 | }
341 | }
342 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/wirelesspienetwork/overview/views/ViewAnimation.java:
--------------------------------------------------------------------------------
1 | package com.wirelesspienetwork.overview.views;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.graphics.Rect;
5 | import com.wirelesspienetwork.overview.misc.ReferenceCountedTrigger;
6 |
7 | public class ViewAnimation {
8 |
9 | public static class OverviewCardEnterContext {
10 | // A trigger to run some logic when all the animations complete. This works around the fact
11 | // that it is difficult to coordinate ViewPropertyAnimators
12 | ReferenceCountedTrigger postAnimationTrigger;
13 | // An update listener to notify as the enter animation progresses (used for the home transition)
14 | ValueAnimator.AnimatorUpdateListener updateListener;
15 |
16 | // These following properties are updated for each task view we start the enter animation on
17 |
18 | // Whether or not the current task occludes the launch target
19 | boolean currentTaskOccludesLaunchTarget;
20 | // The task rect for the current stack
21 | Rect currentTaskRect;
22 | // The transform of the current task view
23 | OverviewCardTransform currentTaskTransform;
24 | // The view index of the current task view
25 | int currentStackViewIndex;
26 | // The total number of task views
27 | int currentStackViewCount;
28 |
29 | public OverviewCardEnterContext(ReferenceCountedTrigger t) {
30 | postAnimationTrigger = t;
31 | }
32 | }
33 |
34 | /* The animation context for a task view animation out of Recents */
35 | public static class OverviewCardExitContext {
36 | // A trigger to run some logic when all the animations complete. This works around the fact
37 | // that it is difficult to coordinate ViewPropertyAnimators
38 | ReferenceCountedTrigger postAnimationTrigger;
39 |
40 | // The translationY to apply to a TaskView to move it off the bottom of the task stack
41 | int offscreenTranslationY;
42 |
43 | public OverviewCardExitContext(ReferenceCountedTrigger t) {
44 | postAnimationTrigger = t;
45 | }
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/lib/src/main/res/anim/recents_launch_prev_affiliated_task_target.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
22 |
23 |
28 |
29 |
35 |
--------------------------------------------------------------------------------
/lib/src/main/res/anim/recents_return_to_launcher_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
23 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/src/main/res/anim/recents_return_to_launcher_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
23 |
28 |
31 |
32 |
--------------------------------------------------------------------------------
/lib/src/main/res/anim/recents_to_launcher_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
23 |
28 |
29 |
--------------------------------------------------------------------------------
/lib/src/main/res/anim/recents_to_launcher_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
23 |
27 |
28 |
--------------------------------------------------------------------------------
/lib/src/main/res/anim/recents_to_search_launcher_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
23 |
28 |
29 |
--------------------------------------------------------------------------------
/lib/src/main/res/anim/recents_to_search_launcher_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
23 |
29 |
30 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/alias.xml:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
22 |
23 | - @android:color/transparent
24 |
25 |
26 | - @android:drawable/stat_sys_warning
27 |
28 |
29 | - @android:drawable/ic_media_pause
30 |
31 |
32 | - @*android:drawable/ic_media_stop
33 |
34 |
35 | - @*android:drawable/ic_contact_picture
36 |
37 |
38 | - @*android:string/ok
39 |
40 |
41 | - "MM:HH:SS"
42 |
43 |
44 | - @*android:bool/config_enableLockScreenRotation
45 |
46 |
47 | - true
48 |
49 |
50 | - @*android:integer/config_activityDefaultDur
51 |
52 |
53 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
22 |
23 |
24 | - 15
25 | - 100
26 |
27 |
28 | - #FFFFFFFF
29 |
30 |
31 | - 73
- 0
32 | - 392
- 0
33 | - 201
- 259
34 | - 442
- 259
35 | - 4
- 703
36 | - 157
- 334
37 | - 0
- 334
38 |
39 |
40 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/bools.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 | true
19 | false
20 | true
21 | true
22 | true
23 |
24 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 | #ffffffff
21 | #ff1d1d1d
22 | @color/system_bar_background_opaque
23 | #ff000000
24 | #66000000
25 | #00000000
26 | #ff000000
27 | #88000000
28 | #ffffffff
29 | #ff090909
30 | #80000000
31 | #99ffffff
32 | #ff33B5E5
33 | #66FFFFFF
34 | #FFFFFFFF
35 | #FFFFFFFF
36 | #FF404040
37 | #ff263238
38 | #ff384248
39 | #ff80CBC4
40 | #fff4511e
41 | #FFFFFFFF
42 | #29ffffff
43 | #B3FFFFFF
44 | #99FFFFFF
45 | #24B0BEC5
46 | #66FFFFFF
47 | #99009688
48 | #99FFFFFF
49 | #33FFFFFF
50 | #FFFFFFFF
51 | #FFFFFFFF
52 | #FFFFFFFF
53 |
54 |
55 | #ff686868
56 |
57 |
58 | #ffe6e6e6
59 |
60 | #ffeeeeee
61 |
62 | #cc000000
63 |
64 | #ffeeeeee
65 |
66 | #99000000
67 |
68 | #28ffffff
69 |
70 | #ffe6e6e6
71 |
72 | #ff666666
73 |
74 | #ffffffff
75 |
76 |
77 | #ff1a1a1a
78 |
79 |
80 | #fffafafa
81 |
82 |
83 | #d4ffffff
84 |
85 |
86 | #ffe0e0e0
87 |
88 |
90 | #ff424242
91 |
92 |
93 | #28000000
94 |
95 |
96 | #30000000
97 |
98 |
99 | #30ffffff
100 |
101 |
102 | @color/system_accent_color
103 |
104 | #99afbdc4
105 |
106 |
107 | @color/system_secondary_color
108 | #FFFFFFFF
109 | #b2FFFFFF
110 | #FFFFFFFF
111 |
112 | #ffffff
113 | #ffbbbbbb
114 |
115 | #77000000
116 | #ff434343
117 |
118 |
119 | #E5FFFFFF
120 |
121 |
122 | #44000000
123 |
124 |
125 | #03000000
126 |
127 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
22 |
23 |
24 |
25 | 1
26 |
27 | 250
28 |
29 | 250
30 |
31 | 225
32 |
34 | 275
35 |
36 | 225
37 |
38 | 125
39 |
41 | 150
42 |
43 | 250
44 |
45 | 12
46 |
47 | 150
48 |
49 | 300
50 |
51 | 400
52 |
53 | 250
54 |
55 | 225
56 |
57 | 96
58 |
59 | 200
60 |
61 | true
62 |
63 | true
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/defaults.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/donottranslate.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/integers.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 | 75
21 | 0
22 |
23 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/internal.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 | @*android:dimen/status_bar_height
19 | @*android:dimen/navigation_bar_height
20 |
21 |
22 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/lland_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 | 380dp
22 | 100dp
23 | 550dp
24 | 40dp
25 | 40dp
26 | 90dp
27 | 12dp
28 | 170dp
29 | 40dp
30 | 20dp
31 | 250dp
32 | 20dp
33 | 10dp
34 | 100dp
35 | 45dp
36 | 30dp
37 | 3dp
38 | 5dp
39 | 30dp
40 | 1000dp
41 | 6dp
42 | 18dp
43 | 18dp
44 | 20dp
45 | 35dp
46 |
47 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/lland_strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 | L Land
22 |
23 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/missing.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 25dp
4 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/overview.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':lib'
2 |
--------------------------------------------------------------------------------