Showing preview only (271K chars total). Download the full file or copy to clipboard to get everything.
Repository: vikramkakkar/DeckView
Branch: master
Commit: 0487563749a8
Files: 74
Total size: 247.1 KB
Directory structure:
gitextract_kqwh3f90/
├── .gitignore
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── appeaser/
│ │ └── deckviewlibrary/
│ │ └── ApplicationTest.java
│ └── main/
│ ├── AndroidManifest.xml
│ └── res/
│ └── values/
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── deckview/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── appeaser/
│ │ └── deckview/
│ │ └── ApplicationTest.java
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── appeaser/
│ │ └── deckview/
│ │ ├── helpers/
│ │ │ ├── DeckChildViewTransform.java
│ │ │ ├── DeckViewConfig.java
│ │ │ ├── DeckViewSwipeHelper.java
│ │ │ └── FakeShadowDrawable.java
│ │ ├── utilities/
│ │ │ ├── DVConstants.java
│ │ │ ├── DVUtils.java
│ │ │ ├── DozeTrigger.java
│ │ │ └── ReferenceCountedTrigger.java
│ │ └── views/
│ │ ├── AnimateableDeckChildViewBounds.java
│ │ ├── DeckChildView.java
│ │ ├── DeckChildViewHeader.java
│ │ ├── DeckChildViewThumbnail.java
│ │ ├── DeckView.java
│ │ ├── DeckViewLayoutAlgorithm.java
│ │ ├── DeckViewScroller.java
│ │ ├── DeckViewTouchHandler.java
│ │ ├── FixedSizeImageView.java
│ │ ├── ViewAnimation.java
│ │ └── ViewPool.java
│ └── res/
│ ├── drawable-v21/
│ │ ├── deck_child_view_button_bg.xml
│ │ ├── deck_child_view_dismiss_dark.xml
│ │ ├── deck_child_view_dismiss_light.xml
│ │ ├── deck_child_view_header_bg.xml
│ │ └── deck_child_view_header_bg_color.xml
│ ├── interpolator-v21/
│ │ ├── decelerate_quint.xml
│ │ ├── fast_out_linear_in.xml
│ │ ├── fast_out_slow_in.xml
│ │ └── linear_out_slow_in.xml
│ ├── layout/
│ │ ├── deck_child_view.xml
│ │ └── deck_child_view_header.xml
│ ├── values/
│ │ ├── colors.xml
│ │ ├── config.xml
│ │ ├── dimens.xml
│ │ └── strings.xml
│ ├── values-land/
│ │ └── dimens.xml
│ ├── values-sw600dp/
│ │ └── dimens.xml
│ ├── values-sw600dp-land/
│ │ └── dimens.xml
│ └── values-sw720dp/
│ └── dimens.xml
├── deckviewsample/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── appeaser/
│ │ └── deckviewsample/
│ │ └── ApplicationTest.java
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── appeaser/
│ │ └── deckviewsample/
│ │ ├── Datum.java
│ │ └── DeckViewSampleActivity.java
│ └── res/
│ ├── drawable-v21/
│ │ └── box.xml
│ ├── layout/
│ │ └── activity_deck_view_sample.xml
│ ├── menu/
│ │ └── menu_deck_view_sample.xml
│ ├── values/
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ ├── values-v21/
│ │ └── styles.xml
│ └── values-w820dp/
│ └── dimens.xml
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
#################
## AndroidStudio
#################
.gradle
project.properties
.idea
gen
*.class
out
*.iml
#################
## Eclipse
#################
*.pydevproject
.project
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
#################
## Visual Studio
#################
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
build/
[Bb]in/
[Oo]bj/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.Publish.xml
*.pubxml
# NuGet Packages Directory
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
#packages/
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.[Pp]ublish.xml
*.pfx
*.publishsettings
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
App_Data/*.mdf
App_Data/*.ldf
#############
## Windows detritus
#############
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Mac crap
.DS_Store
#############
## Python
#############
*.py[co]
# Packages
*.egg
*.egg-info
dist/
build/
eggs/
parts/
var/
sdist/
develop-eggs/
.installed.cfg
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
#Translations
*.mo
#Mr Developer
.mr.developer.cfg
================================================
FILE: README.md
================================================
# DeckView
A ViewGroup that mimics Android (Lollipop) Recent apps screen layout.
######Note:
DeckView is **not** a true recycler. It *does* recycle views - but it also updates progress map for *all* of its children on each scroll step. This will result in lags with large datasets.
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.appeaser.deckviewlibrary"
minSdkVersion 21
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Users\Vikram\Documents\Android\adt-bundle-windows-x86-20130219\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: app/src/androidTest/java/com/appeaser/deckviewlibrary/ApplicationTest.java
================================================
package com.appeaser.deckviewlibrary;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.appeaser.deckviewlibrary">
<application
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme">
</application>
</manifest>
================================================
FILE: app/src/main/res/values/strings.xml
================================================
<resources>
<string name="app_name">DeckViewLibrary</string>
</resources>
================================================
FILE: app/src/main/res/values/styles.xml
================================================
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
</resources>
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.1.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
================================================
FILE: deckview/.gitignore
================================================
/build
================================================
FILE: deckview/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
minSdkVersion 21
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
================================================
FILE: deckview/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:/Users/Vikram/Documents/Android/adt-bundle-windows-x86-20130219/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: deckview/src/androidTest/java/com/appeaser/deckview/ApplicationTest.java
================================================
package com.appeaser.deckview;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}
================================================
FILE: deckview/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.appeaser.deckview">
<application
android:allowBackup="true"
android:label="@string/app_name">
</application>
</manifest>
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/helpers/DeckChildViewTransform.java
================================================
package com.appeaser.deckview.helpers;
import android.animation.ValueAnimator;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.view.animation.Interpolator;
/**
* Created by Vikram on 02/04/2015.
*/
/* The transform state for a task view */
public class DeckChildViewTransform {
public int startDelay = 0;
public int translationY = 0;
public float translationZ = 0;
public float scale = 1f;
public float alpha = 1f;
public boolean visible = false;
public Rect rect = new Rect();
public float p = 0f;
public DeckChildViewTransform() {
// Do nothing
}
public DeckChildViewTransform(DeckChildViewTransform o) {
startDelay = o.startDelay;
translationY = o.translationY;
translationZ = o.translationZ;
scale = o.scale;
alpha = o.alpha;
visible = o.visible;
rect.set(o.rect);
p = o.p;
}
/**
* Resets the current transform
*/
public void reset() {
startDelay = 0;
translationY = 0;
translationZ = 0;
scale = 1f;
alpha = 1f;
visible = false;
rect.setEmpty();
p = 0f;
}
/**
* Convenience functions to compare against current property values
*/
public boolean hasAlphaChangedFrom(float v) {
return (Float.compare(alpha, v) != 0);
}
public boolean hasScaleChangedFrom(float v) {
return (Float.compare(scale, v) != 0);
}
public boolean hasTranslationYChangedFrom(float v) {
return (Float.compare(translationY, v) != 0);
}
public boolean hasTranslationZChangedFrom(float v) {
return (Float.compare(translationZ, v) != 0);
}
/**
* Applies this transform to a view.
*/
public void applyToTaskView(View v, int duration, Interpolator interp, boolean allowLayers,
boolean allowShadows, ValueAnimator.AnimatorUpdateListener updateCallback) {
// Check to see if any properties have changed, and update the task view
if (duration > 0) {
ViewPropertyAnimator anim = v.animate();
boolean requiresLayers = false;
// Animate to the final state
if (hasTranslationYChangedFrom(v.getTranslationY())) {
anim.translationY(translationY);
}
if (allowShadows && hasTranslationZChangedFrom(v.getTranslationZ())) {
anim.translationZ(translationZ);
}
if (hasScaleChangedFrom(v.getScaleX())) {
anim.scaleX(scale)
.scaleY(scale);
requiresLayers = true;
}
if (hasAlphaChangedFrom(v.getAlpha())) {
// Use layers if we animate alpha
anim.alpha(alpha);
requiresLayers = true;
}
if (requiresLayers && allowLayers) {
anim.withLayer();
}
if (updateCallback != null) {
anim.setUpdateListener(updateCallback);
} else {
anim.setUpdateListener(null);
}
anim.setStartDelay(startDelay)
.setDuration(duration)
.setInterpolator(interp)
.start();
} else {
// Set the changed properties
if (hasTranslationYChangedFrom(v.getTranslationY())) {
v.setTranslationY(translationY);
}
if (allowShadows && hasTranslationZChangedFrom(v.getTranslationZ())) {
v.setTranslationZ(translationZ);
}
if (hasScaleChangedFrom(v.getScaleX())) {
v.setScaleX(scale);
v.setScaleY(scale);
}
if (hasAlphaChangedFrom(v.getAlpha())) {
v.setAlpha(alpha);
}
}
}
/**
* Reset the transform on a view.
*/
public static void reset(View v) {
v.setTranslationX(0f);
v.setTranslationY(0f);
v.setTranslationZ(0f);
v.setScaleX(1f);
v.setScaleY(1f);
v.setAlpha(1f);
}
@Override
public String toString() {
return "TaskViewTransform delay: " + startDelay + " y: " + translationY + " z: " + translationZ +
" scale: " + scale + " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
" p: " + p;
}
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/helpers/DeckViewConfig.java
================================================
package com.appeaser.deckview.helpers;
/**
* Created by Vikram on 02/04/2015.
*/
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.appeaser.deckview.R;
import com.appeaser.deckview.utilities.DVConstants;
/**
* Configuration helper
*/
public class DeckViewConfig {
static DeckViewConfig sInstance;
static int sPrevConfigurationHashCode;
/**
* Levels of svelte in increasing severity/austerity.
*/
// No svelting.
public static final int SVELTE_NONE = 0;
// Limit thumbnail cache to number of visible thumbnails when Recents was loaded, disable
// caching thumbnails as you scroll.
public static final int SVELTE_LIMIT_CACHE = 1;
// Disable the thumbnail cache, load thumbnails asynchronously when the activity loads and
// evict all thumbnails when hidden.
public static final int SVELTE_DISABLE_CACHE = 2;
// Disable all thumbnail loading.
public static final int SVELTE_DISABLE_LOADING = 3;
/**
* Animations
*/
public float animationPxMovementPerSecond;
/**
* Interpolators
*/
public Interpolator fastOutSlowInInterpolator;
public Interpolator fastOutLinearInInterpolator;
public Interpolator linearOutSlowInInterpolator;
public Interpolator quintOutInterpolator;
/**
* Filtering
*/
public int filteringCurrentViewsAnimDuration;
public int filteringNewViewsAnimDuration;
/**
* Insets
*/
public Rect systemInsets = new Rect();
public Rect displayRect = new Rect();
/**
* Layout
*/
boolean isLandscape;
/**
* Task stack
*/
public int taskStackScrollDuration;
public int taskStackMaxDim;
public int taskStackTopPaddingPx;
public float taskStackWidthPaddingPct;
public float taskStackOverscrollPct;
/**
* Transitions
*/
public int transitionEnterFromAppDelay;
public int transitionEnterFromHomeDelay;
/**
* Task view animation and styles
*/
public int taskViewEnterFromAppDuration;
public int taskViewEnterFromHomeDuration;
public int taskViewEnterFromHomeStaggerDelay;
public int taskViewExitToAppDuration;
public int taskViewExitToHomeDuration;
public int taskViewRemoveAnimDuration;
public int taskViewRemoveAnimTranslationXPx;
public int taskViewTranslationZMinPx;
public int taskViewTranslationZMaxPx;
public int taskViewRoundedCornerRadiusPx;
public int taskViewHighlightPx;
public int taskViewAffiliateGroupEnterOffsetPx;
public float taskViewThumbnailAlpha;
/**
* Task bar colors
*/
public int taskBarViewDefaultBackgroundColor;
public int taskBarViewLightTextColor;
public int taskBarViewDarkTextColor;
public int taskBarViewHighlightColor;
public float taskBarViewAffiliationColorMinAlpha;
/**
* Task bar size & animations
*/
public int taskBarHeight;
public int taskBarDismissDozeDelaySeconds;
/**
* Nav bar scrim
*/
public int navBarScrimEnterDuration;
/**
* Launch states
*/
public boolean launchedWithAltTab;
public boolean launchedWithNoRecentTasks;
public boolean launchedFromAppWithThumbnail;
public boolean launchedFromHome;
public boolean launchedFromSearchHome;
public boolean launchedReuseTaskStackViews;
public boolean launchedHasConfigurationChanged;
public int launchedToTaskId;
public int launchedNumVisibleTasks;
public int launchedNumVisibleThumbnails;
/**
* Misc *
*/
public boolean useHardwareLayers;
public int altTabKeyDelay;
public boolean fakeShadows;
/**
* Dev options and global settings
*/
public boolean debugModeEnabled;
public int svelteLevel;
/**
* Private constructor
*/
private DeckViewConfig(Context context) {
// Properties that don't have to be reloaded with each configuration change can be loaded
// here.
// Interpolators
fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
R.interpolator.fast_out_slow_in);
fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
R.interpolator.fast_out_linear_in);
linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
R.interpolator.linear_out_slow_in);
quintOutInterpolator = AnimationUtils.loadInterpolator(context,
R.interpolator.decelerate_quint);
}
/**
* Updates the configuration to the current context
*/
public static DeckViewConfig reinitialize(Context context) {
if (sInstance == null) {
sInstance = new DeckViewConfig(context);
}
int configHashCode = context.getResources().getConfiguration().hashCode();
if (sPrevConfigurationHashCode != configHashCode) {
sInstance.update(context);
sPrevConfigurationHashCode = configHashCode;
}
sInstance.updateOnReinitialize(context);
return sInstance;
}
/**
* Returns the current recents configuration
*/
public static DeckViewConfig getInstance() {
return sInstance;
}
/**
* Updates the state, given the specified context
*/
void update(Context context) {
SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
Resources res = context.getResources();
DisplayMetrics dm = res.getDisplayMetrics();
// Debug mode
debugModeEnabled = settings.getBoolean(DVConstants.Values.App.Key_DebugModeEnabled, false);
// Layout
isLandscape = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
// Insets
displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
// Animations
animationPxMovementPerSecond =
res.getDimensionPixelSize(R.dimen.animation_movement_in_dps_per_second);
// Filtering
filteringCurrentViewsAnimDuration =
res.getInteger(R.integer.filter_animate_current_views_duration);
filteringNewViewsAnimDuration =
res.getInteger(R.integer.filter_animate_new_views_duration);
// Task stack
taskStackScrollDuration =
res.getInteger(R.integer.animate_deck_scroll_duration);
TypedValue widthPaddingPctValue = new TypedValue();
res.getValue(R.dimen.deck_width_padding_percentage, widthPaddingPctValue, true);
taskStackWidthPaddingPct = widthPaddingPctValue.getFloat();
TypedValue stackOverscrollPctValue = new TypedValue();
res.getValue(R.dimen.deck_overscroll_percentage, stackOverscrollPctValue, true);
taskStackOverscrollPct = stackOverscrollPctValue.getFloat();
taskStackMaxDim = res.getInteger(R.integer.max_deck_view_dim);
taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.deck_top_padding);
// Transition
transitionEnterFromAppDelay =
res.getInteger(R.integer.enter_from_app_transition_duration);
transitionEnterFromHomeDelay =
res.getInteger(R.integer.enter_from_home_transition_duration);
// Task view animation and styles
taskViewEnterFromAppDuration =
res.getInteger(R.integer.task_enter_from_app_duration);
taskViewEnterFromHomeDuration =
res.getInteger(R.integer.task_enter_from_home_duration);
taskViewEnterFromHomeStaggerDelay =
res.getInteger(R.integer.task_enter_from_home_stagger_delay);
taskViewExitToAppDuration =
res.getInteger(R.integer.task_exit_to_app_duration);
taskViewExitToHomeDuration =
res.getInteger(R.integer.task_exit_to_home_duration);
taskViewRemoveAnimDuration =
res.getInteger(R.integer.animate_task_view_remove_duration);
taskViewRemoveAnimTranslationXPx =
res.getDimensionPixelSize(R.dimen.task_view_remove_anim_translation_x);
taskViewRoundedCornerRadiusPx =
res.getDimensionPixelSize(R.dimen.task_view_rounded_corners_radius);
taskViewHighlightPx = res.getDimensionPixelSize(R.dimen.task_view_highlight);
taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.task_view_z_min);
taskViewTranslationZMaxPx = res.getDimensionPixelSize(R.dimen.task_view_z_max);
taskViewAffiliateGroupEnterOffsetPx =
res.getDimensionPixelSize(R.dimen.task_view_affiliate_group_enter_offset);
TypedValue thumbnailAlphaValue = new TypedValue();
res.getValue(R.dimen.task_view_thumbnail_alpha, thumbnailAlphaValue, true);
taskViewThumbnailAlpha = thumbnailAlphaValue.getFloat();
// Task bar colors
taskBarViewDefaultBackgroundColor =
res.getColor(R.color.task_bar_default_background_color);
taskBarViewLightTextColor =
res.getColor(R.color.task_bar_light_text_color);
taskBarViewDarkTextColor =
res.getColor(R.color.task_bar_dark_text_color);
taskBarViewHighlightColor =
res.getColor(R.color.task_bar_highlight_color);
TypedValue affMinAlphaPctValue = new TypedValue();
res.getValue(R.dimen.task_affiliation_color_min_alpha_percentage, affMinAlphaPctValue, true);
taskBarViewAffiliationColorMinAlpha = affMinAlphaPctValue.getFloat();
// Task bar size & animations
taskBarHeight = res.getDimensionPixelSize(R.dimen.deck_child_header_bar_height);
taskBarDismissDozeDelaySeconds =
res.getInteger(R.integer.task_bar_dismiss_delay_seconds);
// Nav bar scrim
navBarScrimEnterDuration =
res.getInteger(R.integer.nav_bar_scrim_enter_duration);
// Misc
useHardwareLayers = res.getBoolean(R.bool.config_use_hardware_layers);
altTabKeyDelay = res.getInteger(R.integer.deck_alt_tab_key_delay);
fakeShadows = res.getBoolean(R.bool.config_fake_shadows);
svelteLevel = res.getInteger(R.integer.deck_svelte_level);
}
/**
* Updates the system insets
*/
public void updateSystemInsets(Rect insets) {
systemInsets.set(insets);
}
/**
* Updates the search bar app widget
*/
public void updateSearchBarAppWidgetId(Context context, int appWidgetId) {
}
/**
* Updates the states that need to be re-read whenever we re-initialize.
*/
void updateOnReinitialize(Context context/*, SystemServicesProxy ssp*/) {
}
/**
* Called when the configuration has changed, and we want to reset any configuration specific
* members.
*/
public void updateOnConfigurationChange() {
// Reset this flag on configuration change to ensure that we recreate new task views
launchedReuseTaskStackViews = false;
// Set this flag to indicate that the configuration has changed since Recents last launched
launchedHasConfigurationChanged = true;
}
/**
* Returns the task stack bounds in the current orientation. These bounds do not account for
* the system insets.
*/
public void getTaskStackBounds(int windowWidth, int windowHeight, int topInset, int rightInset,
Rect taskStackBounds) {
taskStackBounds.set(0, 0, windowWidth, windowHeight);
}
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/helpers/DeckViewSwipeHelper.java
================================================
package com.appeaser.deckview.helpers;
/**
* Created by Vikram on 02/04/2015.
*/
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.animation.LinearInterpolator;
/**
* This class facilitates swipe to dismiss. It defines an interface to be implemented by the
* by the class hosting the views that need to swiped, and, using this interface, handles touch
* events and translates / fades / animates the view as it is dismissed.
*/
public class DeckViewSwipeHelper {
static final String TAG = "DeckViewSwipeHelper";
private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
private static final boolean CONSTRAIN_SWIPE = true;
private static final boolean FADE_OUT_DURING_SWIPE = true;
private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
public static final int X = 0;
public static final int Y = 1;
private static LinearInterpolator sLinearInterpolator = new LinearInterpolator();
private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
private int DEFAULT_ESCAPE_ANIMATION_DURATION = 75; // ms
private int MAX_ESCAPE_ANIMATION_DURATION = 150; // ms
private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 250; // ms
public static float ALPHA_FADE_START = 0.15f; // fraction of thumbnail width
// where fade starts
static final float ALPHA_FADE_END = 0.65f; // fraction of thumbnail width
// beyond which alpha->0
private float mMinAlpha = 0f;
private float mPagingTouchSlop;
Callback mCallback;
private int mSwipeDirection;
private VelocityTracker mVelocityTracker;
private float mInitialTouchPos;
private boolean mDragging;
private View mCurrView;
private boolean mCanCurrViewBeDimissed;
private float mDensityScale;
public boolean mAllowSwipeTowardsStart = true;
public boolean mAllowSwipeTowardsEnd = true;
private boolean mRtl;
public DeckViewSwipeHelper(int swipeDirection, Callback callback, float densityScale,
float pagingTouchSlop) {
mCallback = callback;
mSwipeDirection = swipeDirection;
mVelocityTracker = VelocityTracker.obtain();
mDensityScale = densityScale;
mPagingTouchSlop = pagingTouchSlop;
}
public void setDensityScale(float densityScale) {
mDensityScale = densityScale;
}
public void setPagingTouchSlop(float pagingTouchSlop) {
mPagingTouchSlop = pagingTouchSlop;
}
public void cancelOngoingDrag() {
if (mDragging) {
if (mCurrView != null) {
mCallback.onDragCancelled(mCurrView);
setTranslation(mCurrView, 0);
mCallback.onSnapBackCompleted(mCurrView);
mCurrView = null;
}
mDragging = false;
}
}
public void resetTranslation(View v) {
setTranslation(v, 0);
}
private float getPos(MotionEvent ev) {
return mSwipeDirection == X ? ev.getX() : ev.getY();
}
private float getTranslation(View v) {
return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY();
}
private float getVelocity(VelocityTracker vt) {
return mSwipeDirection == X ? vt.getXVelocity() :
vt.getYVelocity();
}
private ObjectAnimator createTranslationAnimation(View v, float newPos) {
ObjectAnimator anim = ObjectAnimator.ofFloat(v,
mSwipeDirection == X ? View.TRANSLATION_X : View.TRANSLATION_Y, newPos);
return anim;
}
private float getPerpendicularVelocity(VelocityTracker vt) {
return mSwipeDirection == X ? vt.getYVelocity() :
vt.getXVelocity();
}
private void setTranslation(View v, float translate) {
if (mSwipeDirection == X) {
v.setTranslationX(translate);
} else {
v.setTranslationY(translate);
}
}
private float getSize(View v) {
final DisplayMetrics dm = v.getContext().getResources().getDisplayMetrics();
return mSwipeDirection == X ? dm.widthPixels : dm.heightPixels;
}
public void setMinAlpha(float minAlpha) {
mMinAlpha = minAlpha;
}
float getAlphaForOffset(View view) {
float viewSize = getSize(view);
final float fadeSize = ALPHA_FADE_END * viewSize;
float result = 1.0f;
float pos = getTranslation(view);
if (pos >= viewSize * ALPHA_FADE_START) {
result = 1.0f - (pos - viewSize * ALPHA_FADE_START) / fadeSize;
} else if (pos < viewSize * (1.0f - ALPHA_FADE_START)) {
result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
}
result = Math.min(result, 1.0f);
result = Math.max(result, 0f);
return Math.max(mMinAlpha, result);
}
/**
* Determines whether the given view has RTL layout.
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static boolean isLayoutRtl(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return View.LAYOUT_DIRECTION_RTL == view.getLayoutDirection();
} else {
return false;
}
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mDragging = false;
mCurrView = mCallback.getChildAtPosition(ev);
mVelocityTracker.clear();
if (mCurrView != null) {
mRtl = isLayoutRtl(mCurrView);
mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
mVelocityTracker.addMovement(ev);
mInitialTouchPos = getPos(ev);
} else {
mCanCurrViewBeDimissed = false;
}
break;
case MotionEvent.ACTION_MOVE:
if (mCurrView != null) {
mVelocityTracker.addMovement(ev);
float pos = getPos(ev);
float delta = pos - mInitialTouchPos;
if (Math.abs(delta) > mPagingTouchSlop) {
mCallback.onBeginDrag(mCurrView);
mDragging = true;
mInitialTouchPos = pos - getTranslation(mCurrView);
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mDragging = false;
mCurrView = null;
break;
}
return mDragging;
}
/**
* @param view The view to be dismissed
* @param velocity The desired pixels/second speed at which the view should move
*/
private void dismissChild(final View view, float velocity) {
final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
float newPos;
if (velocity < 0
|| (velocity == 0 && getTranslation(view) < 0)
// if we use the Menu to dismiss an item in landscape, animate up
|| (velocity == 0 && getTranslation(view) == 0 && mSwipeDirection == Y)) {
newPos = -getSize(view);
} else {
newPos = getSize(view);
}
int duration = MAX_ESCAPE_ANIMATION_DURATION;
if (velocity != 0) {
duration = Math.min(duration,
(int) (Math.abs(newPos - getTranslation(view)) *
1000f / Math.abs(velocity)));
} else {
duration = DEFAULT_ESCAPE_ANIMATION_DURATION;
}
ValueAnimator anim = createTranslationAnimation(view, newPos);
anim.setInterpolator(sLinearInterpolator);
anim.setDuration(duration);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mCallback.onChildDismissed(view);
if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
view.setAlpha(1.f);
}
}
});
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
view.setAlpha(getAlphaForOffset(view));
}
}
});
anim.start();
}
private void snapChild(final View view, float velocity) {
final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
ValueAnimator anim = createTranslationAnimation(view, 0);
int duration = SNAP_ANIM_LEN;
anim.setDuration(duration);
anim.setInterpolator(DeckViewConfig.getInstance().linearOutSlowInInterpolator);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
view.setAlpha(getAlphaForOffset(view));
}
mCallback.onSwipeChanged(mCurrView, view.getTranslationX());
}
});
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
view.setAlpha(1.0f);
}
mCallback.onSnapBackCompleted(view);
}
});
anim.start();
}
public boolean onTouchEvent(MotionEvent ev) {
if (!mDragging) {
if (!onInterceptTouchEvent(ev)) {
return mCanCurrViewBeDimissed;
}
}
mVelocityTracker.addMovement(ev);
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_MOVE:
if (mCurrView != null) {
float delta = getPos(ev) - mInitialTouchPos;
setSwipeAmount(delta);
mCallback.onSwipeChanged(mCurrView, delta);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mCurrView != null) {
endSwipe(mVelocityTracker);
}
break;
}
return true;
}
private void setSwipeAmount(float amount) {
// don't let items that can't be dismissed be dragged more than
// maxScrollDistance
if (CONSTRAIN_SWIPE
&& (!isValidSwipeDirection(amount) || !mCallback.canChildBeDismissed(mCurrView))) {
float size = getSize(mCurrView);
float maxScrollDistance = 0.15f * size;
if (Math.abs(amount) >= size) {
amount = amount > 0 ? maxScrollDistance : -maxScrollDistance;
} else {
amount = maxScrollDistance * (float) Math.sin((amount / size) * (Math.PI / 2));
}
}
setTranslation(mCurrView, amount);
if (FADE_OUT_DURING_SWIPE && mCanCurrViewBeDimissed) {
float alpha = getAlphaForOffset(mCurrView);
mCurrView.setAlpha(alpha);
}
}
private boolean isValidSwipeDirection(float amount) {
if (mSwipeDirection == X) {
if (mRtl) {
return (amount <= 0) ? mAllowSwipeTowardsEnd : mAllowSwipeTowardsStart;
} else {
return (amount <= 0) ? mAllowSwipeTowardsStart : mAllowSwipeTowardsEnd;
}
}
// Vertical swipes are always valid.
return true;
}
private void endSwipe(VelocityTracker velocityTracker) {
velocityTracker.computeCurrentVelocity(1000 /* px/sec */);
float velocity = getVelocity(velocityTracker);
float perpendicularVelocity = getPerpendicularVelocity(velocityTracker);
float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
float translation = getTranslation(mCurrView);
// Decide whether to dismiss the current view
boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
Math.abs(translation) > 0.6 * getSize(mCurrView);
boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
(Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
(velocity > 0) == (translation > 0);
boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
&& isValidSwipeDirection(translation)
&& (childSwipedFastEnough || childSwipedFarEnough);
if (dismissChild) {
// flingadingy
dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
} else {
// snappity
mCallback.onDragCancelled(mCurrView);
snapChild(mCurrView, velocity);
}
}
public interface Callback {
View getChildAtPosition(MotionEvent ev);
boolean canChildBeDismissed(View v);
void onBeginDrag(View v);
void onSwipeChanged(View v, float delta);
void onChildDismissed(View v);
void onSnapBackCompleted(View v);
void onDragCancelled(View v);
}
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/helpers/FakeShadowDrawable.java
================================================
package com.appeaser.deckview.helpers;
/**
* Created by Vikram on 01/04/2015.
*/
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.util.Log;
import com.appeaser.deckview.R;
/**
* A rounded rectangle drawable which also includes a shadow around. This is mostly copied from
* frameworks/support/v7/cardview/eclair-mr1/android/support/v7/widget/
* RoundRectDrawableWithShadow.java revision c42ba8c000d1e6ce85e152dfc17089a0a69e739f with a few
* modifications to suit our needs in SystemUI.
*/
public class FakeShadowDrawable extends Drawable {
// used to calculate content padding
final static double COS_45 = Math.cos(Math.toRadians(45));
final static float SHADOW_MULTIPLIER = 1.5f;
final float mInsetShadow; // extra shadow to avoid gaps between card and shadow
Paint mCornerShadowPaint;
Paint mEdgeShadowPaint;
final RectF mCardBounds;
float mCornerRadius;
Path mCornerShadowPath;
// updated value with inset
float mMaxShadowSize;
// actual value set by developer
float mRawMaxShadowSize;
// multiplied value to account for shadow offset
float mShadowSize;
// actual value set by developer
float mRawShadowSize;
private boolean mDirty = true;
private final int mShadowStartColor;
private final int mShadowEndColor;
private boolean mAddPaddingForCorners = true;
/**
* If shadow size is set to a value above max shadow, we print a warning
*/
private boolean mPrintedShadowClipWarning = false;
public FakeShadowDrawable(Resources resources, DeckViewConfig config) {
mShadowStartColor = resources.getColor(R.color.fake_shadow_start_color);
mShadowEndColor = resources.getColor(R.color.fake_shadow_end_color);
mInsetShadow = resources.getDimension(R.dimen.fake_shadow_inset);
setShadowSize(resources.getDimensionPixelSize(R.dimen.fake_shadow_size),
resources.getDimensionPixelSize(R.dimen.fake_shadow_size));
mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mCornerShadowPaint.setStyle(Paint.Style.FILL);
mCornerShadowPaint.setDither(true);
mCornerRadius = config.taskViewRoundedCornerRadiusPx;
mCardBounds = new RectF();
mEdgeShadowPaint = new Paint(mCornerShadowPaint);
}
@Override
public void setAlpha(int alpha) {
mCornerShadowPaint.setAlpha(alpha);
mEdgeShadowPaint.setAlpha(alpha);
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mDirty = true;
}
void setShadowSize(float shadowSize, float maxShadowSize) {
if (shadowSize < 0 || maxShadowSize < 0) {
throw new IllegalArgumentException("invalid shadow size");
}
if (shadowSize > maxShadowSize) {
shadowSize = maxShadowSize;
if (!mPrintedShadowClipWarning) {
Log.w("CardView", "Shadow size is being clipped by the max shadow size. See "
+ "{CardView#setMaxCardElevation}.");
mPrintedShadowClipWarning = true;
}
}
if (mRawShadowSize == shadowSize && mRawMaxShadowSize == maxShadowSize) {
return;
}
mRawShadowSize = shadowSize;
mRawMaxShadowSize = maxShadowSize;
mShadowSize = shadowSize * SHADOW_MULTIPLIER + mInsetShadow;
mMaxShadowSize = maxShadowSize + mInsetShadow;
mDirty = true;
invalidateSelf();
}
@Override
public boolean getPadding(Rect padding) {
int vOffset = (int) Math.ceil(calculateVerticalPadding(mRawMaxShadowSize, mCornerRadius,
mAddPaddingForCorners));
int hOffset = (int) Math.ceil(calculateHorizontalPadding(mRawMaxShadowSize, mCornerRadius,
mAddPaddingForCorners));
padding.set(hOffset, vOffset, hOffset, vOffset);
return true;
}
static float calculateVerticalPadding(float maxShadowSize, float cornerRadius,
boolean addPaddingForCorners) {
if (addPaddingForCorners) {
return (float) (maxShadowSize * SHADOW_MULTIPLIER + (1 - COS_45) * cornerRadius);
} else {
return maxShadowSize * SHADOW_MULTIPLIER;
}
}
static float calculateHorizontalPadding(float maxShadowSize, float cornerRadius,
boolean addPaddingForCorners) {
if (addPaddingForCorners) {
return (float) (maxShadowSize + (1 - COS_45) * cornerRadius);
} else {
return maxShadowSize;
}
}
@Override
public void setColorFilter(ColorFilter cf) {
mCornerShadowPaint.setColorFilter(cf);
mEdgeShadowPaint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
@Override
public void draw(Canvas canvas) {
if (mDirty) {
buildComponents(getBounds());
mDirty = false;
}
canvas.translate(0, mRawShadowSize / 4);
drawShadow(canvas);
canvas.translate(0, -mRawShadowSize / 4);
}
private void drawShadow(Canvas canvas) {
final float edgeShadowTop = -mCornerRadius - mShadowSize;
final float inset = mCornerRadius + mInsetShadow + mRawShadowSize / 2;
final boolean drawHorizontalEdges = mCardBounds.width() - 2 * inset > 0;
final boolean drawVerticalEdges = mCardBounds.height() - 2 * inset > 0;
// LT
int saved = canvas.save();
canvas.translate(mCardBounds.left + inset, mCardBounds.top + inset);
canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
if (drawHorizontalEdges) {
canvas.drawRect(0, edgeShadowTop,
mCardBounds.width() - 2 * inset, -mCornerRadius,
mEdgeShadowPaint);
}
canvas.restoreToCount(saved);
// RB
saved = canvas.save();
canvas.translate(mCardBounds.right - inset, mCardBounds.bottom - inset);
canvas.rotate(180f);
canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
if (drawHorizontalEdges) {
canvas.drawRect(0, edgeShadowTop,
mCardBounds.width() - 2 * inset, -mCornerRadius + mShadowSize,
mEdgeShadowPaint);
}
canvas.restoreToCount(saved);
// LB
saved = canvas.save();
canvas.translate(mCardBounds.left + inset, mCardBounds.bottom - inset);
canvas.rotate(270f);
canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
if (drawVerticalEdges) {
canvas.drawRect(0, edgeShadowTop,
mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint);
}
canvas.restoreToCount(saved);
// RT
saved = canvas.save();
canvas.translate(mCardBounds.right - inset, mCardBounds.top + inset);
canvas.rotate(90f);
canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
if (drawVerticalEdges) {
canvas.drawRect(0, edgeShadowTop,
mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint);
}
canvas.restoreToCount(saved);
}
private void buildShadowCorners() {
RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius);
RectF outerBounds = new RectF(innerBounds);
outerBounds.inset(-mShadowSize, -mShadowSize);
if (mCornerShadowPath == null) {
mCornerShadowPath = new Path();
} else {
mCornerShadowPath.reset();
}
mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD);
mCornerShadowPath.moveTo(-mCornerRadius, 0);
mCornerShadowPath.rLineTo(-mShadowSize, 0);
// outer arc
mCornerShadowPath.arcTo(outerBounds, 180f, 90f, false);
// inner arc
mCornerShadowPath.arcTo(innerBounds, 270f, -90f, false);
mCornerShadowPath.close();
float startRatio = mCornerRadius / (mCornerRadius + mShadowSize);
mCornerShadowPaint.setShader(new RadialGradient(0, 0, mCornerRadius + mShadowSize,
new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
new float[]{0f, startRatio, 1f}
, Shader.TileMode.CLAMP));
// we offset the content shadowSize/2 pixels up to make it more realistic.
// this is why edge shadow shader has some extra space
// When drawing bottom edge shadow, we use that extra space.
mEdgeShadowPaint.setShader(new LinearGradient(0, -mCornerRadius + mShadowSize, 0,
-mCornerRadius - mShadowSize,
new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
new float[]{0f, .5f, 1f}, Shader.TileMode.CLAMP));
}
private void buildComponents(Rect bounds) {
// Card is offset SHADOW_MULTIPLIER * maxShadowSize to account for the shadow shift.
// We could have different top-bottom offsets to avoid extra gap above but in that case
// center aligning Views inside the CardView would be problematic.
final float verticalOffset = mMaxShadowSize * SHADOW_MULTIPLIER;
mCardBounds.set(bounds.left + mMaxShadowSize, bounds.top + verticalOffset,
bounds.right - mMaxShadowSize, bounds.bottom - verticalOffset);
buildShadowCorners();
}
float getMinWidth() {
final float content = 2 *
Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow + mRawMaxShadowSize / 2);
return content + (mRawMaxShadowSize + mInsetShadow) * 2;
}
float getMinHeight() {
final float content = 2 * Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow
+ mRawMaxShadowSize * SHADOW_MULTIPLIER / 2);
return content + (mRawMaxShadowSize * SHADOW_MULTIPLIER + mInsetShadow) * 2;
}
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/utilities/DVConstants.java
================================================
package com.appeaser.deckview.utilities;
/**
* Created by Vikram on 02/04/2015.
*/
public class DVConstants {
public static class DebugFlags {
// Enable this with any other debug flag to see more info
public static final boolean Verbose = false;
public static class App {
// Enables debug drawing for the transition thumbnail
public static final boolean EnableTransitionThumbnailDebugMode = false;
// Enables the filtering of tasks according to their grouping
public static final boolean EnableTaskFiltering = false;
// Enables clipping of tasks against each other
public static final boolean EnableTaskStackClipping = true;
// Enables tapping on the TaskBar to launch the task
public static final boolean EnableTaskBarTouchEvents = true;
// Enables app-info pane on long-pressing the icon
public static final boolean EnableDevAppInfoOnLongPress = true;
// Enables debug mode
public static final boolean EnableDebugMode = false;
// Enables the search bar layout
public static final boolean EnableSearchLayout = true;
// Enables the thumbnail alpha on the front-most task
public static final boolean EnableThumbnailAlphaOnFrontmost = false;
// This disables the bitmap and icon caches
public static final boolean DisableBackgroundCache = false;
// Enables the simulated task affiliations
public static final boolean EnableSimulatedTaskGroups = false;
// Defines the number of mock task affiliations per group
public static final int TaskAffiliationsGroupCount = 12;
// Enables us to create mock recents tasks
public static final boolean EnableSystemServicesProxy = false;
// Defines the number of mock recents packages to create
public static final int SystemServicesProxyMockPackageCount = 3;
// Defines the number of mock recents tasks to create
public static final int SystemServicesProxyMockTaskCount = 100;
}
}
public static class Values {
public static class App {
public static int AppWidgetHostId = 1024;
public static String Key_SearchAppWidgetId = "searchAppWidgetId";
public static String Key_DebugModeEnabled = "debugModeEnabled";
public static String DebugModeVersion = "A";
}
public static class DView {
public static final int TaskStackMinOverscrollRange = 32;
public static final int TaskStackMaxOverscrollRange = 128;
public static final int FilterStartDelay = 25;
}
}
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/utilities/DVUtils.java
================================================
package com.appeaser.deckview.utilities;
import android.animation.Animator;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.View;
import com.appeaser.deckview.helpers.DeckViewConfig;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
* Created by Vikram on 02/04/2015.
*/
public class DVUtils {
// Reflection methods for altering shadows
private static Method sPropertyMethod;
static {
try {
Class<?> c = Class.forName("android.view.GLES20Canvas");
sPropertyMethod = c.getDeclaredMethod("setProperty", String.class, String.class);
if (!sPropertyMethod.isAccessible()) sPropertyMethod.setAccessible(true);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
/**
* Calculates a consistent animation duration (ms) for all animations depending on the movement
* of the object being animated.
*/
public static int calculateTranslationAnimationDuration(int distancePx) {
return calculateTranslationAnimationDuration(distancePx, 100);
}
public static int calculateTranslationAnimationDuration(int distancePx, int minDuration) {
DeckViewConfig config = DeckViewConfig.getInstance();
return Math.max(minDuration, (int) (1000f /* ms/s */ *
(Math.abs(distancePx) / config.animationPxMovementPerSecond)));
}
/**
* Scales a rect about its centroid
*/
public static void scaleRectAboutCenter(Rect r, float scale) {
if (scale != 1.0f) {
int cx = r.centerX();
int cy = r.centerY();
r.offset(-cx, -cy);
r.left = (int) (r.left * scale + 0.5f);
r.top = (int) (r.top * scale + 0.5f);
r.right = (int) (r.right * scale + 0.5f);
r.bottom = (int) (r.bottom * scale + 0.5f);
r.offset(cx, cy);
}
}
/**
* Maps a coorindate in a descendant view into the parent.
*/
public static float mapCoordInDescendentToSelf(View descendant, View root,
float[] coord, boolean includeRootScroll) {
ArrayList<View> ancestorChain = new ArrayList<View>();
float[] pt = {coord[0], coord[1]};
View v = descendant;
while (v != root && v != null) {
ancestorChain.add(v);
v = (View) v.getParent();
}
ancestorChain.add(root);
float scale = 1.0f;
int count = ancestorChain.size();
for (int i = 0; i < count; i++) {
View v0 = ancestorChain.get(i);
// For TextViews, scroll has a meaning which relates to the text position
// which is very strange... ignore the scroll.
if (v0 != descendant || includeRootScroll) {
pt[0] -= v0.getScrollX();
pt[1] -= v0.getScrollY();
}
v0.getMatrix().mapPoints(pt);
pt[0] += v0.getLeft();
pt[1] += v0.getTop();
scale *= v0.getScaleX();
}
coord[0] = pt[0];
coord[1] = pt[1];
return scale;
}
/**
* Maps a coordinate in the root to a descendent.
*/
public static float mapCoordInSelfToDescendent(View descendant, View root,
float[] coord, Matrix tmpInverseMatrix) {
ArrayList<View> ancestorChain = new ArrayList<View>();
float[] pt = {coord[0], coord[1]};
View v = descendant;
while (v != root) {
ancestorChain.add(v);
v = (View) v.getParent();
}
ancestorChain.add(root);
float scale = 1.0f;
int count = ancestorChain.size();
tmpInverseMatrix.set(IDENTITY_MATRIX);
for (int i = count - 1; i >= 0; i--) {
View ancestor = ancestorChain.get(i);
View next = i > 0 ? ancestorChain.get(i - 1) : null;
pt[0] += ancestor.getScrollX();
pt[1] += ancestor.getScrollY();
if (next != null) {
pt[0] -= next.getLeft();
pt[1] -= next.getTop();
next.getMatrix().invert(tmpInverseMatrix);
tmpInverseMatrix.mapPoints(pt);
scale *= next.getScaleX();
}
}
coord[0] = pt[0];
coord[1] = pt[1];
return scale;
}
/**
* Calculates the constrast between two colors, using the algorithm provided by the WCAG v2.
*/
public static float computeContrastBetweenColors(int bg, int fg) {
float bgR = Color.red(bg) / 255f;
float bgG = Color.green(bg) / 255f;
float bgB = Color.blue(bg) / 255f;
bgR = (bgR < 0.03928f) ? bgR / 12.92f : (float) Math.pow((bgR + 0.055f) / 1.055f, 2.4f);
bgG = (bgG < 0.03928f) ? bgG / 12.92f : (float) Math.pow((bgG + 0.055f) / 1.055f, 2.4f);
bgB = (bgB < 0.03928f) ? bgB / 12.92f : (float) Math.pow((bgB + 0.055f) / 1.055f, 2.4f);
float bgL = 0.2126f * bgR + 0.7152f * bgG + 0.0722f * bgB;
float fgR = Color.red(fg) / 255f;
float fgG = Color.green(fg) / 255f;
float fgB = Color.blue(fg) / 255f;
fgR = (fgR < 0.03928f) ? fgR / 12.92f : (float) Math.pow((fgR + 0.055f) / 1.055f, 2.4f);
fgG = (fgG < 0.03928f) ? fgG / 12.92f : (float) Math.pow((fgG + 0.055f) / 1.055f, 2.4f);
fgB = (fgB < 0.03928f) ? fgB / 12.92f : (float) Math.pow((fgB + 0.055f) / 1.055f, 2.4f);
float fgL = 0.2126f * fgR + 0.7152f * fgG + 0.0722f * fgB;
return Math.abs((fgL + 0.05f) / (bgL + 0.05f));
}
/**
* Returns the base color overlaid with another overlay color with a specified alpha.
*/
public static int getColorWithOverlay(int baseColor, int overlayColor, float overlayAlpha) {
return Color.rgb(
(int) (overlayAlpha * Color.red(baseColor) +
(1f - overlayAlpha) * Color.red(overlayColor)),
(int) (overlayAlpha * Color.green(baseColor) +
(1f - overlayAlpha) * Color.green(overlayColor)),
(int) (overlayAlpha * Color.blue(baseColor) +
(1f - overlayAlpha) * Color.blue(overlayColor)));
}
/**
* Sets some private shadow properties.
*/
public static void setShadowProperty(String property, String value)
throws IllegalAccessException, InvocationTargetException {
sPropertyMethod.invoke(null, property, value);
}
/**
* Cancels an animation ensuring that if it has listeners, onCancel and onEnd
* are not called.
*/
public static void cancelAnimationWithoutCallbacks(Animator animator) {
if (animator != null) {
animator.removeAllListeners();
animator.cancel();
}
}
public static Matrix IDENTITY_MATRIX = new Matrix() {
void oops() {
throw new IllegalStateException("Matrix can not be modified");
}
@Override
public void set(Matrix src) {
oops();
}
@Override
public void reset() {
oops();
}
@Override
public void setTranslate(float dx, float dy) {
oops();
}
@Override
public void setScale(float sx, float sy, float px, float py) {
oops();
}
@Override
public void setScale(float sx, float sy) {
oops();
}
@Override
public void setRotate(float degrees, float px, float py) {
oops();
}
@Override
public void setRotate(float degrees) {
oops();
}
@Override
public void setSinCos(float sinValue, float cosValue, float px, float py) {
oops();
}
@Override
public void setSinCos(float sinValue, float cosValue) {
oops();
}
@Override
public void setSkew(float kx, float ky, float px, float py) {
oops();
}
@Override
public void setSkew(float kx, float ky) {
oops();
}
@Override
public boolean setConcat(Matrix a, Matrix b) {
oops();
return false;
}
@Override
public boolean preTranslate(float dx, float dy) {
oops();
return false;
}
@Override
public boolean preScale(float sx, float sy, float px, float py) {
oops();
return false;
}
@Override
public boolean preScale(float sx, float sy) {
oops();
return false;
}
@Override
public boolean preRotate(float degrees, float px, float py) {
oops();
return false;
}
@Override
public boolean preRotate(float degrees) {
oops();
return false;
}
@Override
public boolean preSkew(float kx, float ky, float px, float py) {
oops();
return false;
}
@Override
public boolean preSkew(float kx, float ky) {
oops();
return false;
}
@Override
public boolean preConcat(Matrix other) {
oops();
return false;
}
@Override
public boolean postTranslate(float dx, float dy) {
oops();
return false;
}
@Override
public boolean postScale(float sx, float sy, float px, float py) {
oops();
return false;
}
@Override
public boolean postScale(float sx, float sy) {
oops();
return false;
}
@Override
public boolean postRotate(float degrees, float px, float py) {
oops();
return false;
}
@Override
public boolean postRotate(float degrees) {
oops();
return false;
}
@Override
public boolean postSkew(float kx, float ky, float px, float py) {
oops();
return false;
}
@Override
public boolean postSkew(float kx, float ky) {
oops();
return false;
}
@Override
public boolean postConcat(Matrix other) {
oops();
return false;
}
@Override
public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) {
oops();
return false;
}
@Override
public boolean setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex,
int pointCount) {
oops();
return false;
}
@Override
public void setValues(float[] values) {
oops();
}
};
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/utilities/DozeTrigger.java
================================================
package com.appeaser.deckview.utilities;
/**
* Created by Vikram on 01/04/2015.
*/
import android.os.Handler;
/**
* A dozer is a class that fires a trigger after it falls asleep. You can occasionally poke it to
* wake it up, but it will fall asleep if left untouched.
*/
public class DozeTrigger {
Handler mHandler;
boolean mIsDozing;
boolean mHasTriggered;
int mDozeDurationSeconds;
Runnable mSleepRunnable;
// Sleep-runnable
Runnable mDozeRunnable = new Runnable() {
@Override
public void run() {
mSleepRunnable.run();
mIsDozing = false;
mHasTriggered = true;
}
};
public DozeTrigger(int dozeDurationSeconds, Runnable sleepRunnable) {
mHandler = new Handler();
mDozeDurationSeconds = dozeDurationSeconds;
mSleepRunnable = sleepRunnable;
}
/**
* Starts dozing. This also resets the trigger flag.
*/
public void startDozing() {
forcePoke();
mHasTriggered = false;
}
/**
* Stops dozing.
*/
public void stopDozing() {
mHandler.removeCallbacks(mDozeRunnable);
mIsDozing = false;
}
/**
* Poke this dozer to wake it up for a little bit, if it is dozing.
*/
public void poke() {
if (mIsDozing) {
forcePoke();
}
}
/**
* Poke this dozer to wake it up for a little bit.
*/
void forcePoke() {
mHandler.removeCallbacks(mDozeRunnable);
mHandler.postDelayed(mDozeRunnable, mDozeDurationSeconds * 1000);
mIsDozing = true;
}
/**
* Returns whether we are dozing or not.
*/
public boolean isDozing() {
return mIsDozing;
}
/**
* Returns whether the trigger has fired at least once.
*/
public boolean hasTriggered() {
return mHasTriggered;
}
/**
* Resets the doze trigger state.
*/
public void resetTrigger() {
mHasTriggered = false;
}
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/utilities/ReferenceCountedTrigger.java
================================================
package com.appeaser.deckview.utilities;
/**
* Created by Vikram on 01/04/2015.
*/
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import java.util.ArrayList;
/**
* A ref counted trigger that does some logic when the count is first incremented, or last
* decremented. Not thread safe as it's not currently needed.
*/
public class ReferenceCountedTrigger {
Context mContext;
int mCount;
ArrayList<Runnable> mFirstIncRunnables = new ArrayList<Runnable>();
ArrayList<Runnable> mLastDecRunnables = new ArrayList<Runnable>();
Runnable mErrorRunnable;
// Convenience runnables
Runnable mIncrementRunnable = new Runnable() {
@Override
public void run() {
increment();
}
};
Runnable mDecrementRunnable = new Runnable() {
@Override
public void run() {
decrement();
}
};
public ReferenceCountedTrigger(Context context, Runnable firstIncRunnable,
Runnable lastDecRunnable, Runnable errorRunanable) {
mContext = context;
if (firstIncRunnable != null) mFirstIncRunnables.add(firstIncRunnable);
if (lastDecRunnable != null) mLastDecRunnables.add(lastDecRunnable);
mErrorRunnable = errorRunanable;
}
/**
* Increments the ref count
*/
public void increment() {
if (mCount == 0 && !mFirstIncRunnables.isEmpty()) {
int numRunnables = mFirstIncRunnables.size();
for (int i = 0; i < numRunnables; i++) {
mFirstIncRunnables.get(i).run();
}
}
mCount++;
}
/**
* Convenience method to increment this trigger as a runnable
*/
public Runnable incrementAsRunnable() {
return mIncrementRunnable;
}
/**
* Adds a runnable to the last-decrement runnables list.
*/
public void addLastDecrementRunnable(Runnable r) {
// To ensure that the last decrement always calls, we increment and decrement after setting
// the last decrement runnable
boolean ensureLastDecrement = (mCount == 0);
if (ensureLastDecrement) increment();
mLastDecRunnables.add(r);
if (ensureLastDecrement) decrement();
}
/**
* Decrements the ref count
*/
public void decrement() {
mCount--;
if (mCount == 0 && !mLastDecRunnables.isEmpty()) {
int numRunnables = mLastDecRunnables.size();
for (int i = 0; i < numRunnables; i++) {
mLastDecRunnables.get(i).run();
}
} else if (mCount < 0) {
if (mErrorRunnable != null) {
mErrorRunnable.run();
} else {
new Throwable("Invalid ref count").printStackTrace();
//Console.logError(mContext, "Invalid ref count");
}
}
}
/**
* Convenience method to decrement this trigger as a runnable.
*/
public Runnable decrementAsRunnable() {
return mDecrementRunnable;
}
/**
* Convenience method to decrement this trigger as a animator listener.
*/
public Animator.AnimatorListener decrementOnAnimationEnd() {
return new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
decrement();
}
};
}
/**
* Returns the current ref count
*/
public int getCount() {
return mCount;
}
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/views/AnimateableDeckChildViewBounds.java
================================================
package com.appeaser.deckview.views;
import android.graphics.Outline;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewOutlineProvider;
import com.appeaser.deckview.helpers.DeckViewConfig;
/**
* Created by Vikram on 02/04/2015.
*/
/* An outline provider that has a clip and outline that can be animated. */
public class AnimateableDeckChildViewBounds extends ViewOutlineProvider {
DeckViewConfig mConfig;
DeckChildView mSourceView;
Rect mClipRect = new Rect();
Rect mClipBounds = new Rect();
int mCornerRadius;
float mAlpha = 1f;
final float mMinAlpha = 0.25f;
public AnimateableDeckChildViewBounds(DeckChildView source, int cornerRadius) {
mConfig = DeckViewConfig.getInstance();
mSourceView = source;
mCornerRadius = cornerRadius;
setClipBottom(getClipBottom());
}
@Override
public void getOutline(View view, Outline outline) {
outline.setAlpha(mMinAlpha + mAlpha / (1f - mMinAlpha));
outline.setRoundRect(mClipRect.left, mClipRect.top,
mSourceView.getWidth() - mClipRect.right,
mSourceView.getHeight() - mClipRect.bottom,
mCornerRadius);
}
/**
* Sets the view outline alpha.
*/
void setAlpha(float alpha) {
if (Float.compare(alpha, mAlpha) != 0) {
mAlpha = alpha;
mSourceView.invalidateOutline();
}
}
/**
* Sets the bottom clip.
*/
public void setClipBottom(int bottom) {
if (bottom != mClipRect.bottom) {
mClipRect.bottom = bottom;
mSourceView.invalidateOutline();
updateClipBounds();
if (!mConfig.useHardwareLayers) {
mSourceView.mThumbnailView.updateThumbnailVisibility(
bottom - mSourceView.getPaddingBottom());
}
}
}
/**
* Returns the bottom clip.
*/
public int getClipBottom() {
return mClipRect.bottom;
}
private void updateClipBounds() {
mClipBounds.set(mClipRect.left, mClipRect.top,
mSourceView.getWidth() - mClipRect.right,
mSourceView.getHeight() - mClipRect.bottom);
mSourceView.setClipBounds(mClipBounds);
}
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/views/DeckChildView.java
================================================
package com.appeaser.deckview.views;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import com.appeaser.deckview.R;
import com.appeaser.deckview.helpers.DeckChildViewTransform;
import com.appeaser.deckview.helpers.DeckViewConfig;
import com.appeaser.deckview.helpers.FakeShadowDrawable;
import com.appeaser.deckview.utilities.DVConstants;
import com.appeaser.deckview.utilities.DVUtils;
/**
* Created by Vikram on 02/04/2015.
*/
/* A task view */
public class DeckChildView<T> extends FrameLayout implements
View.OnClickListener, View.OnLongClickListener {
/**
* The TaskView callbacks
*/
interface DeckChildViewCallbacks<T> {
public void onDeckChildViewAppIconClicked(DeckChildView dcv);
public void onDeckChildViewAppInfoClicked(DeckChildView dcv);
public void onDeckChildViewClicked(DeckChildView<T> dcv, T key);
public void onDeckChildViewDismissed(DeckChildView<T> dcv);
public void onDeckChildViewClipStateChanged(DeckChildView dcv);
public void onDeckChildViewFocusChanged(DeckChildView<T> dcv, boolean focused);
}
DeckViewConfig mConfig;
float mTaskProgress;
ObjectAnimator mTaskProgressAnimator;
float mMaxDimScale;
int mDimAlpha;
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator(1f);
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
Paint mDimLayerPaint = new Paint();
T mKey;
boolean mTaskDataLoaded;
boolean mIsFocused;
boolean mFocusAnimationsEnabled;
boolean mClipViewInStack;
AnimateableDeckChildViewBounds mViewBounds;
View mContent;
DeckChildViewThumbnail mThumbnailView;
DeckChildViewHeader mHeaderView;
DeckChildViewCallbacks<T> mCb;
public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
// Optimizations
ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setTaskProgress((Float) animation.getAnimatedValue());
}
};
public DeckChildView(Context context) {
this(context, null);
}
public DeckChildView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DeckChildView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public DeckChildView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = DeckViewConfig.getInstance();
mMaxDimScale = mConfig.taskStackMaxDim / 255f;
mClipViewInStack = true;
mViewBounds = new AnimateableDeckChildViewBounds(this, mConfig.taskViewRoundedCornerRadiusPx);
setTaskProgress(getTaskProgress());
setDim(getDim());
if (mConfig.fakeShadows) {
setBackground(new FakeShadowDrawable(context.getResources(), mConfig));
}
setOutlineProvider(mViewBounds);
}
/**
* Set callback
*/
void setCallbacks(DeckChildViewCallbacks cb) {
mCb = cb;
}
/**
* Resets this TaskView for reuse.
*/
void reset() {
resetViewProperties();
resetNoUserInteractionState();
setClipViewInStack(false);
setCallbacks(null);
}
/**
* Gets the task
*/
T getAttachedKey() {
return mKey;
}
/**
* Returns the view bounds.
*/
AnimateableDeckChildViewBounds getViewBounds() {
return mViewBounds;
}
@Override
protected void onFinishInflate() {
// Bind the views
mContent = findViewById(R.id.task_view_content);
mHeaderView = (DeckChildViewHeader) findViewById(R.id.task_view_bar);
mThumbnailView = (DeckChildViewThumbnail) findViewById(R.id.task_view_thumbnail);
mThumbnailView.updateClipToTaskBar(mHeaderView);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int widthWithoutPadding = width - getPaddingLeft() - getPaddingRight();
int heightWithoutPadding = height - getPaddingTop() - getPaddingBottom();
// Measure the content
mContent.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY));
// Measure the bar view, and action button
mHeaderView.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mConfig.taskBarHeight, MeasureSpec.EXACTLY));
// Measure the thumbnail to be square
mThumbnailView.measure(
MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY));
setMeasuredDimension(width, height);
invalidateOutline();
}
/**
* Synchronizes this view's properties with the task's transform
*/
void updateViewPropertiesToTaskTransform(DeckChildViewTransform toTransform, int duration) {
updateViewPropertiesToTaskTransform(toTransform, duration, null);
}
void updateViewPropertiesToTaskTransform(DeckChildViewTransform toTransform, int duration,
ValueAnimator.AnimatorUpdateListener updateCallback) {
// Apply the transform
toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false,
!mConfig.fakeShadows, updateCallback);
// Update the task progress
DVUtils.cancelAnimationWithoutCallbacks(mTaskProgressAnimator);
if (duration <= 0) {
setTaskProgress(toTransform.p);
} else {
mTaskProgressAnimator = ObjectAnimator.ofFloat(this, "taskProgress", toTransform.p);
mTaskProgressAnimator.setDuration(duration);
mTaskProgressAnimator.addUpdateListener(mUpdateDimListener);
mTaskProgressAnimator.start();
}
}
/**
* Resets this view's properties
*/
void resetViewProperties() {
setDim(0);
setLayerType(View.LAYER_TYPE_NONE, null);
DeckChildViewTransform.reset(this);
}
/**
* When we are un/filtering, this method will set up the transform that we are animating to,
* in order to hide the task.
*/
void prepareTaskTransformForFilterTaskHidden(DeckChildViewTransform toTransform) {
// Fade the view out and slide it away
toTransform.alpha = 0f;
toTransform.translationY += 200;
toTransform.translationZ = 0;
}
/**
* When we are un/filtering, this method will setup the transform that we are animating from,
* in order to show the task.
*/
void prepareTaskTransformForFilterTaskVisible(DeckChildViewTransform fromTransform) {
// Fade the view in
fromTransform.alpha = 0f;
}
/**
* Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time.
*/
void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask,
boolean occludesLaunchTarget, int offscreenY) {
int initialDim = getDim();
if (mConfig.launchedHasConfigurationChanged) {
// Just load the views as-is
} else if (mConfig.launchedFromAppWithThumbnail) {
if (isTaskViewLaunchTargetTask) {
// Set the dim to 0 so we can animate it in
initialDim = 0;
} else if (occludesLaunchTarget) {
// Move the task view off screen (below) so we can animate it in
setTranslationY(offscreenY);
}
} else if (mConfig.launchedFromHome) {
// Move the task view off screen (below) so we can animate it in
setTranslationY(offscreenY);
setTranslationZ(0);
setScaleX(1f);
setScaleY(1f);
}
// Apply the current dim
setDim(initialDim);
// Prepare the thumbnail view alpha
mThumbnailView.prepareEnterRecentsAnimation(isTaskViewLaunchTargetTask);
}
/**
* Animates this task view as it enters recents
*/
void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
Log.i(getClass().getSimpleName(), "startEnterRecentsAnimation");
final DeckChildViewTransform transform = ctx.currentTaskTransform;
int startDelay = 0;
if (mConfig.launchedFromHome) {
Log.i(getClass().getSimpleName(), "mConfig.launchedFromHome false");
// Animate the tasks up
int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
int delay = mConfig.transitionEnterFromHomeDelay +
frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay;
setScaleX(transform.scale);
setScaleY(transform.scale);
if (!mConfig.fakeShadows) {
animate().translationZ(transform.translationZ);
}
animate()
.translationY(transform.translationY)
.setStartDelay(delay)
.setUpdateListener(ctx.updateListener)
.setInterpolator(mConfig.quintOutInterpolator)
.setDuration(mConfig.taskViewEnterFromHomeDuration +
frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay)
.withEndAction(new Runnable() {
@Override
public void run() {
// Decrement the post animation trigger
ctx.postAnimationTrigger.decrement();
}
})
.start();
ctx.postAnimationTrigger.increment();
startDelay = delay;
}
// Enable the focus animations from this point onwards so that they aren't affected by the
// window transitions
postDelayed(new Runnable() {
@Override
public void run() {
enableFocusAnimations();
}
}, startDelay);
}
/**
* Animates this task view as it leaves recents by pressing home.
*/
void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
animate()
.translationY(ctx.offscreenTranslationY)
.setStartDelay(0)
.setUpdateListener(null)
.setInterpolator(mConfig.fastOutLinearInInterpolator)
.setDuration(mConfig.taskViewExitToHomeDuration)
.withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
.start();
ctx.postAnimationTrigger.increment();
}
/**
* Animates this task view as it exits recents
*/
void startLaunchTaskAnimation(final Runnable postAnimRunnable, boolean isLaunchingTask,
boolean occludesLaunchTarget, boolean lockToTask) {
if (isLaunchingTask) {
// Animate the thumbnail alpha back into full opacity for the window animation out
mThumbnailView.startLaunchTaskAnimation(postAnimRunnable);
// Animate the dim
if (mDimAlpha > 0) {
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
anim.setDuration(mConfig.taskViewExitToAppDuration);
anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
anim.start();
}
} else {
// Hide the dismiss button
mHeaderView.startLaunchTaskDismissAnimation();
// If this is another view in the task grouping and is in front of the launch task,
// animate it away first
if (occludesLaunchTarget) {
animate().alpha(0f)
.translationY(getTranslationY() + mConfig.taskViewAffiliateGroupEnterOffsetPx)
.setStartDelay(0)
.setUpdateListener(null)
.setInterpolator(mConfig.fastOutLinearInInterpolator)
.setDuration(mConfig.taskViewExitToAppDuration)
.start();
}
}
}
/**
* Animates the deletion of this task view
*/
void startDeleteTaskAnimation(final Runnable r) {
// Disabling clipping with the stack while the view is animating away
setClipViewInStack(false);
animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
.alpha(0f)
.setStartDelay(0)
.setUpdateListener(null)
.setInterpolator(mConfig.fastOutSlowInInterpolator)
.setDuration(mConfig.taskViewRemoveAnimDuration)
.withEndAction(new Runnable() {
@Override
public void run() {
// We just throw this into a runnable because starting a view property
// animation using layers can cause inconsisten results if we try and
// update the layers while the animation is running. In some cases,
// the runnabled passed in may start an animation which also uses layers
// so we defer all this by posting this.
r.run();
// Re-enable clipping with the stack (we will reuse this view)
setClipViewInStack(true);
}
})
.start();
}
/**
* Animates this task view if the user does not interact with the stack after a certain time.
*/
void startNoUserInteractionAnimation() {
mHeaderView.startNoUserInteractionAnimation();
}
/**
* Mark this task view that the user does has not interacted with the stack after a certain time.
*/
void setNoUserInteractionState() {
mHeaderView.setNoUserInteractionState();
}
/**
* Resets the state tracking that the user has not interacted with the stack after a certain time.
*/
void resetNoUserInteractionState() {
mHeaderView.resetNoUserInteractionState();
}
/**
* Dismisses this task.
*/
void dismissTask() {
// Animate out the view and call the callback
final DeckChildView<T> tv = this;
startDeleteTaskAnimation(new Runnable() {
@Override
public void run() {
if (mCb != null) {
mCb.onDeckChildViewDismissed(tv);
}
}
});
}
/**
* Returns whether this view should be clipped, or any views below should clip against this
* view.
*/
boolean shouldClipViewInStack() {
return mClipViewInStack && (getVisibility() == View.VISIBLE);
}
/**
* Sets whether this view should be clipped, or clipped against.
*/
public void setClipViewInStack(boolean clip) {
if (clip != mClipViewInStack) {
mClipViewInStack = clip;
if (mCb != null) {
mCb.onDeckChildViewClipStateChanged(this);
}
}
}
/**
* Sets the current task progress.
*/
public void setTaskProgress(float p) {
mTaskProgress = p;
mViewBounds.setAlpha(p);
updateDimFromTaskProgress();
}
/**
* Returns the current task progress.
*/
public float getTaskProgress() {
return mTaskProgress;
}
/**
* Returns the current dim.
*/
public void setDim(int dim) {
mDimAlpha = dim;
if (mConfig.useHardwareLayers) {
// Defer setting hardware layers if we have not yet measured, or there is no dim to draw
if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) {
mDimColorFilter =
new PorterDuffColorFilter(Color.argb(mDimAlpha, 0, 0, 0),
PorterDuff.Mode.SRC_ATOP);
mDimLayerPaint.setColorFilter(mDimColorFilter);
mContent.setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
}
} else {
float dimAlpha = mDimAlpha / 255.0f;
if (mThumbnailView != null) {
mThumbnailView.setDimAlpha(dimAlpha);
}
if (mHeaderView != null) {
mHeaderView.setDimAlpha(dim);
}
}
}
/**
* Returns the current dim.
*/
public int getDim() {
return mDimAlpha;
}
/**
* Animates the dim to the task progress.
*/
void animateDimToProgress(int delay, int duration, Animator.AnimatorListener postAnimRunnable) {
// Animate the dim into view as well
int toDim = getDimFromTaskProgress();
if (toDim != getDim()) {
ObjectAnimator anim = ObjectAnimator.ofInt(DeckChildView.this, "dim", toDim);
anim.setStartDelay(delay);
anim.setDuration(duration);
if (postAnimRunnable != null) {
anim.addListener(postAnimRunnable);
}
anim.start();
}
}
/**
* Compute the dim as a function of the scale of this view.
*/
int getDimFromTaskProgress() {
float dim = mMaxDimScale * mDimInterpolator.getInterpolation(1f - mTaskProgress);
return (int) (dim * 255);
}
/**
* Update the dim as a function of the scale of this view.
*/
void updateDimFromTaskProgress() {
setDim(getDimFromTaskProgress());
}
/**** View focus state ****/
/**
* Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
* if the view is not currently visible, or we are in touch state (where we still want to keep
* track of focus).
*/
public void setFocusedTask(boolean animateFocusedState) {
mIsFocused = true;
if (mFocusAnimationsEnabled) {
// Focus the header bar
mHeaderView.onTaskViewFocusChanged(true, animateFocusedState);
}
// Update the thumbnail alpha with the focus
mThumbnailView.onFocusChanged(true);
// Call the callback
if (mCb != null) {
mCb.onDeckChildViewFocusChanged(this, true);
}
// Workaround, we don't always want it focusable in touch mode, but we want the first task
// to be focused after the enter-recents animation, which can be triggered from either touch
// or keyboard
setFocusableInTouchMode(true);
requestFocus();
setFocusableInTouchMode(false);
invalidate();
}
/**
* Unsets the focused task explicitly.
*/
void unsetFocusedTask() {
mIsFocused = false;
if (mFocusAnimationsEnabled) {
// Un-focus the header bar
mHeaderView.onTaskViewFocusChanged(false, true);
}
// Update the thumbnail alpha with the focus
mThumbnailView.onFocusChanged(false);
// Call the callback
if (mCb != null) {
mCb.onDeckChildViewFocusChanged(this, false);
}
invalidate();
}
/**
* Updates the explicitly focused state when the view focus changes.
*/
@Override
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
if (!gainFocus) {
unsetFocusedTask();
}
}
/**
* Returns whether we have explicitly been focused.
*/
public boolean isFocusedTask() {
return mIsFocused || isFocused();
}
/**
* Enables all focus animations.
*/
void enableFocusAnimations() {
boolean wasFocusAnimationsEnabled = mFocusAnimationsEnabled;
mFocusAnimationsEnabled = true;
if (mIsFocused && !wasFocusAnimationsEnabled) {
// Re-notify the header if we were focused and animations were not previously enabled
mHeaderView.onTaskViewFocusChanged(true, true);
}
}
/**** TaskCallbacks Implementation ****/
/**
* Binds this task view to the task
*/
public void onTaskBound(T key) {
mKey = key;
}
private boolean isBound() {
return mKey != null;
}
/**
* Binds this task view to the task
*/
public void onTaskUnbound() {
mKey = null;
}
public Bitmap getThumbnail() {
if (mThumbnailView != null) {
return mThumbnailView.getThumbnail();
}
return null;
}
public void onDataLoaded(T key, Bitmap thumbnail, Drawable headerIcon,
String headerTitle, int headerBgColor) {
if (!isBound() || !mKey.equals(key))
return;
if (mThumbnailView != null && mHeaderView != null) {
// Bind each of the views to the new task data
mThumbnailView.rebindToTask(thumbnail);
mHeaderView.rebindToTask(headerIcon, headerTitle, headerBgColor);
// Rebind any listeners
mHeaderView.mApplicationIcon.setOnClickListener(this);
mHeaderView.mDismissButton.setOnClickListener(this);
// TODO: Check if this functionality is needed
mHeaderView.mApplicationIcon.setOnLongClickListener(this);
}
mTaskDataLoaded = true;
}
public void onDataUnloaded() {
if (mThumbnailView != null && mHeaderView != null) {
// Unbind each of the views from the task data and remove the task callback
mThumbnailView.unbindFromTask();
mHeaderView.unbindFromTask();
// Unbind any listeners
mHeaderView.mApplicationIcon.setOnClickListener(null);
mHeaderView.mDismissButton.setOnClickListener(null);
if (DVConstants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
mHeaderView.mApplicationIcon.setOnLongClickListener(null);
}
}
mTaskDataLoaded = false;
}
/**
* Enables/disables handling touch on this task view.
*/
public void setTouchEnabled(boolean enabled) {
setOnClickListener(enabled ? this : null);
}
/**
* * View.OnClickListener Implementation ***
*/
@Override
public void onClick(final View v) {
final DeckChildView<T> tv = this;
final boolean delayViewClick = (v != this);
if (delayViewClick) {
// We purposely post the handler delayed to allow for the touch feedback to draw
postDelayed(new Runnable() {
@Override
public void run() {
if (DVConstants.DebugFlags.App.EnableTaskFiltering
&& v == mHeaderView.mApplicationIcon) {
if (mCb != null) {
mCb.onDeckChildViewAppIconClicked(tv);
}
} else if (v == mHeaderView.mDismissButton) {
dismissTask();
}
}
}, 125);
} else {
if (mCb != null) {
mCb.onDeckChildViewClicked(tv, tv.getAttachedKey());
}
}
}
/**
* * View.OnLongClickListener Implementation ***
*/
@Override
public boolean onLongClick(View v) {
if (v == mHeaderView.mApplicationIcon) {
if (mCb != null) {
mCb.onDeckChildViewAppInfoClicked(this);
return true;
}
}
return false;
}
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/views/DeckChildViewHeader.java
================================================
package com.appeaser.deckview.views;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import com.appeaser.deckview.R;
import com.appeaser.deckview.helpers.DeckViewConfig;
import com.appeaser.deckview.utilities.DVConstants;
import com.appeaser.deckview.utilities.DVUtils;
/**
* Created by Vikram on 02/04/2015.
*/
/* The task bar view */
public class DeckChildViewHeader extends FrameLayout {
DeckViewConfig mConfig;
// Header views
ImageView mDismissButton;
ImageView mApplicationIcon;
TextView mActivityDescription;
// Header drawables
boolean mCurrentPrimaryColorIsDark;
int mCurrentPrimaryColor;
int mBackgroundColor;
Drawable mLightDismissDrawable;
Drawable mDarkDismissDrawable;
RippleDrawable mBackground;
GradientDrawable mBackgroundColorDrawable;
AnimatorSet mFocusAnimator;
String mDismissContentDescription;
// Static highlight that we draw at the top of each view
static Paint sHighlightPaint;
// Header dim, which is only used when task view hardware layers are not used
Paint mDimLayerPaint = new Paint();
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
public DeckChildViewHeader(Context context) {
this(context, null);
}
public DeckChildViewHeader(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DeckChildViewHeader(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public DeckChildViewHeader(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = DeckViewConfig.getInstance();
setWillNotDraw(false);
setClipToOutline(true);
setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRect(0, 0, getMeasuredWidth(), getMeasuredHeight());
}
});
// Load the dismiss resources
Resources res = context.getResources();
mLightDismissDrawable = res.getDrawable(R.drawable.deck_child_view_dismiss_light);
mDarkDismissDrawable = res.getDrawable(R.drawable.deck_child_view_dismiss_dark);
mDismissContentDescription =
res.getString(R.string.accessibility_item_will_be_dismissed);
// Configure the highlight paint
if (sHighlightPaint == null) {
sHighlightPaint = new Paint();
sHighlightPaint.setStyle(Paint.Style.STROKE);
sHighlightPaint.setStrokeWidth(mConfig.taskViewHighlightPx);
sHighlightPaint.setColor(mConfig.taskBarViewHighlightColor);
sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
sHighlightPaint.setAntiAlias(true);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// We ignore taps on the task bar except on the filter and dismiss buttons
if (!DVConstants.DebugFlags.App.EnableTaskBarTouchEvents) return true;
return super.onTouchEvent(event);
}
@Override
protected void onFinishInflate() {
// Initialize the icon and description views
mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
mActivityDescription = (TextView) findViewById(R.id.activity_description);
mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
// Hide the backgrounds if they are ripple drawables
if (!DVConstants.DebugFlags.App.EnableTaskFiltering) {
if (mApplicationIcon.getBackground() instanceof RippleDrawable) {
mApplicationIcon.setBackground(null);
}
}
mBackgroundColorDrawable = (GradientDrawable) getContext().getDrawable(R.drawable
.deck_child_view_header_bg_color);
// Copy the ripple drawable since we are going to be manipulating it
mBackground = (RippleDrawable)
getContext().getDrawable(R.drawable.deck_child_view_header_bg);
mBackground = (RippleDrawable) mBackground.mutate().getConstantState().newDrawable();
mBackground.setColor(ColorStateList.valueOf(0));
mBackground.setDrawableByLayerId(mBackground.getId(0), mBackgroundColorDrawable);
setBackground(mBackground);
}
@Override
protected void onDraw(Canvas canvas) {
// Draw the highlight at the top edge (but put the bottom edge just out of view)
float offset = (float) Math.ceil(mConfig.taskViewHighlightPx / 2f);
float radius = mConfig.taskViewRoundedCornerRadiusPx;
int count = canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight());
canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
canvas.restoreToCount(count);
}
@Override
public boolean hasOverlappingRendering() {
return false;
}
/**
* Sets the dim alpha, only used when we are not using hardware layers.
* (see RecentsConfiguration.useHardwareLayers)
*/
void setDimAlpha(int alpha) {
mDimColorFilter = new PorterDuffColorFilter(Color.argb(alpha, 0, 0, 0),
PorterDuff.Mode.SRC_ATOP);
mDimLayerPaint.setColorFilter(mDimColorFilter);
setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
}
/**
* Returns the secondary color for a primary color.
*/
int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) {
int overlayColor = useLightOverlayColor ? Color.WHITE : Color.BLACK;
return DVUtils.getColorWithOverlay(primaryColor, overlayColor, 0.8f);
}
/**
* Binds the bar view to the task
*/
//public void rebindToTask(Task t) {
public void rebindToTask(Drawable headerIcon, String headerTitle, int headerBgColor) {
// If an activity icon is defined, then we use that as the primary icon to show in the bar,
// otherwise, we fall back to the application icon
mApplicationIcon.setImageDrawable(headerIcon);
mApplicationIcon.setContentDescription(headerTitle);
mActivityDescription.setText(headerTitle);
// Try and apply the system ui tint
int existingBgColor = (getBackground() instanceof ColorDrawable) ?
((ColorDrawable) getBackground()).getColor() : 0;
if (existingBgColor != headerBgColor) {
mBackgroundColorDrawable.setColor(headerBgColor);
mBackgroundColor = headerBgColor;
}
mCurrentPrimaryColor = headerBgColor;
//mCurrentPrimaryColorIsDark = t.useLightOnPrimaryColor;
mActivityDescription.setTextColor(mConfig.taskBarViewLightTextColor);
mDismissButton.setImageDrawable(mLightDismissDrawable);
mDismissButton.setContentDescription(String.format(mDismissContentDescription,
headerTitle));
}
/**
* Unbinds the bar view from the task
*/
void unbindFromTask() {
mApplicationIcon.setImageDrawable(null);
}
/**
* Animates this task bar dismiss button when launching a task.
*/
void startLaunchTaskDismissAnimation() {
if (mDismissButton.getVisibility() == View.VISIBLE) {
mDismissButton.animate().cancel();
mDismissButton.animate()
.alpha(0f)
.setStartDelay(0)
.setInterpolator(mConfig.fastOutSlowInInterpolator)
.setDuration(mConfig.taskViewExitToAppDuration)
.withLayer()
.start();
}
}
/**
* Animates this task bar if the user does not interact with the stack after a certain time.
*/
void startNoUserInteractionAnimation() {
if (mDismissButton.getVisibility() != View.VISIBLE) {
mDismissButton.setVisibility(View.VISIBLE);
mDismissButton.setAlpha(0f);
mDismissButton.animate()
.alpha(1f)
.setStartDelay(0)
.setInterpolator(mConfig.fastOutLinearInInterpolator)
.setDuration(mConfig.taskViewEnterFromAppDuration)
.withLayer()
.start();
}
}
/**
* Mark this task view that the user does has not interacted with the stack after a certain time.
*/
void setNoUserInteractionState() {
if (mDismissButton.getVisibility() != View.VISIBLE) {
mDismissButton.animate().cancel();
mDismissButton.setVisibility(View.VISIBLE);
mDismissButton.setAlpha(1f);
}
}
/**
* Resets the state tracking that the user has not interacted with the stack after a certain time.
*/
void resetNoUserInteractionState() {
mDismissButton.setVisibility(View.INVISIBLE);
}
@Override
protected int[] onCreateDrawableState(int extraSpace) {
// Don't forward our state to the drawable - we do it manually in onTaskViewFocusChanged.
// This is to prevent layer trashing when the view is pressed.
return new int[]{};
}
/**
* Notifies the associated TaskView has been focused.
*/
void onTaskViewFocusChanged(boolean focused, boolean animateFocusedState) {
// If we are not animating the visible state, just return
if (!animateFocusedState) return;
boolean isRunning = false;
if (mFocusAnimator != null) {
isRunning = mFocusAnimator.isRunning();
DVUtils.cancelAnimationWithoutCallbacks(mFocusAnimator);
}
if (focused) {
int secondaryColor = getSecondaryColor(mCurrentPrimaryColor, mCurrentPrimaryColorIsDark);
int[][] states = new int[][]{
new int[]{android.R.attr.state_enabled},
new int[]{android.R.attr.state_pressed}
};
int[] newStates = new int[]{
android.R.attr.state_enabled,
android.R.attr.state_pressed
};
int[] colors = new int[]{
secondaryColor,
secondaryColor
};
mBackground.setColor(new ColorStateList(states, colors));
mBackground.setState(newStates);
// Pulse the background color
int currentColor = mBackgroundColor;
int lightPrimaryColor = getSecondaryColor(mCurrentPrimaryColor, mCurrentPrimaryColorIsDark);
ValueAnimator backgroundColor = ValueAnimator.ofObject(new ArgbEvaluator(),
currentColor, lightPrimaryColor);
backgroundColor.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mBackground.setState(new int[]{});
}
});
backgroundColor.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int color = (int) animation.getAnimatedValue();
mBackgroundColorDrawable.setColor(color);
mBackgroundColor = color;
}
});
backgroundColor.setRepeatCount(ValueAnimator.INFINITE);
backgroundColor.setRepeatMode(ValueAnimator.REVERSE);
// Pulse the translation
ObjectAnimator translation = ObjectAnimator.ofFloat(this, "translationZ", 15f);
translation.setRepeatCount(ValueAnimator.INFINITE);
translation.setRepeatMode(ValueAnimator.REVERSE);
mFocusAnimator = new AnimatorSet();
mFocusAnimator.playTogether(backgroundColor, translation);
mFocusAnimator.setStartDelay(750);
mFocusAnimator.setDuration(750);
mFocusAnimator.start();
} else {
if (isRunning) {
// Restore the background color
int currentColor = mBackgroundColor;
ValueAnimator backgroundColor = ValueAnimator.ofObject(new ArgbEvaluator(),
currentColor, mCurrentPrimaryColor);
backgroundColor.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int color = (int) animation.getAnimatedValue();
mBackgroundColorDrawable.setColor(color);
mBackgroundColor = color;
}
});
// Restore the translation
ObjectAnimator translation = ObjectAnimator.ofFloat(this, "translationZ", 0f);
mFocusAnimator = new AnimatorSet();
mFocusAnimator.playTogether(backgroundColor, translation);
mFocusAnimator.setDuration(150);
mFocusAnimator.start();
} else {
mBackground.setState(new int[]{});
setTranslationZ(0f);
}
}
}
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/views/DeckChildViewThumbnail.java
================================================
package com.appeaser.deckview.views;
/**
* Created by Vikram on 02/04/2015.
*/
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LightingColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
import com.appeaser.deckview.helpers.DeckViewConfig;
import com.appeaser.deckview.utilities.DVUtils;
/**
* The task thumbnail view. It implements an image view that allows for animating the dim and
* alpha of the thumbnail image.
*/
public class DeckChildViewThumbnail extends View {
DeckViewConfig mConfig;
// Drawing
float mDimAlpha;
Matrix mScaleMatrix = new Matrix();
Paint mDrawPaint = new Paint();
RectF mBitmapRect = new RectF();
RectF mLayoutRect = new RectF();
BitmapShader mBitmapShader;
LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
// Thumbnail alpha
float mThumbnailAlpha;
ValueAnimator mThumbnailAlphaAnimator;
ValueAnimator.AnimatorUpdateListener mThumbnailAlphaUpdateListener
= new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mThumbnailAlpha = (float) animation.getAnimatedValue();
updateThumbnailPaintFilter();
}
};
// Task bar clipping, the top of this thumbnail can be clipped against the opaque header
// bar that overlaps this thumbnail
View mTaskBar;
Rect mClipRect = new Rect();
// Visibility optimization, if the thumbnail height is less than the height of the header
// bar for the task view, then just mark this thumbnail view as invisible
boolean mInvisible;
public DeckChildViewThumbnail(Context context) {
this(context, null);
}
public DeckChildViewThumbnail(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DeckChildViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public DeckChildViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = DeckViewConfig.getInstance();
mDrawPaint.setColorFilter(mLightingColorFilter);
mDrawPaint.setFilterBitmap(true);
mDrawPaint.setAntiAlias(true);
}
@Override
protected void onFinishInflate() {
mThumbnailAlpha = mConfig.taskViewThumbnailAlpha;
updateThumbnailPaintFilter();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
mLayoutRect.set(0, 0, getWidth(), getHeight());
updateThumbnailScale();
}
}
@Override
protected void onDraw(Canvas canvas) {
if (mInvisible) {
return;
}
// Draw the thumbnail with the rounded corners
canvas.drawRoundRect(0, 0, getWidth(), getHeight(),
mConfig.taskViewRoundedCornerRadiusPx,
mConfig.taskViewRoundedCornerRadiusPx, mDrawPaint);
}
/**
* Sets the thumbnail to a given bitmap.
*/
void setThumbnail(Bitmap bm) {
mThumbnail = bm;
if (bm != null) {
mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP);
mDrawPaint.setShader(mBitmapShader);
mBitmapRect.set(0, 0, bm.getWidth(), bm.getHeight());
updateThumbnailScale();
} else {
mBitmapShader = null;
mDrawPaint.setShader(null);
}
updateThumbnailPaintFilter();
}
/**
* Updates the paint to draw the thumbnail.
*/
void updateThumbnailPaintFilter() {
if (mInvisible) {
return;
}
int mul = (int) ((1.0f - mDimAlpha) * mThumbnailAlpha * 255);
int add = (int) ((1.0f - mDimAlpha) * (1 - mThumbnailAlpha) * 255);
if (mBitmapShader != null) {
mLightingColorFilter =
new LightingColorFilter(Color.argb(255, mul, mul, mul),
Color.argb(0, add, add, add));
mDrawPaint.setColorFilter(mLightingColorFilter);
mDrawPaint.setColor(0xffffffff);
} else {
int grey = mul + add;
mDrawPaint.setColorFilter(null);
mDrawPaint.setColor(Color.argb(255, grey, grey, grey));
}
invalidate();
}
/**
* Updates the thumbnail shader's scale transform.
*/
void updateThumbnailScale() {
if (mBitmapShader != null) {
mScaleMatrix.setRectToRect(mBitmapRect, mLayoutRect, Matrix.ScaleToFit.FILL);
mBitmapShader.setLocalMatrix(mScaleMatrix);
}
}
/**
* Updates the clip rect based on the given task bar.
*/
void updateClipToTaskBar(View taskBar) {
mTaskBar = taskBar;
int top = (int) Math.max(0, taskBar.getTranslationY() +
taskBar.getMeasuredHeight() - 1);
mClipRect.set(0, top, getMeasuredWidth(), getMeasuredHeight());
setClipBounds(mClipRect);
}
/**
* Updates the visibility of the the thumbnail.
*/
void updateThumbnailVisibility(int clipBottom) {
boolean invisible = mTaskBar != null && (getHeight() - clipBottom) <= mTaskBar.getHeight();
if (invisible != mInvisible) {
mInvisible = invisible;
if (!mInvisible) {
updateThumbnailPaintFilter();
}
invalidate();
}
}
/**
* Sets the dim alpha, only used when we are not using hardware layers.
* (see RecentsConfiguration.useHardwareLayers)
*/
public void setDimAlpha(float dimAlpha) {
mDimAlpha = dimAlpha;
updateThumbnailPaintFilter();
}
/**
* Binds the thumbnail view to the task
*/
//void rebindToTask(Task t) {
void rebindToTask(Bitmap thumbnail) {
if (thumbnail != null) {
setThumbnail(thumbnail);
} else {
setThumbnail(null);
}
}
/**
* Unbinds the thumbnail view from the task
*/
void unbindFromTask() {
setThumbnail(null);
}
Bitmap mThumbnail;
public Bitmap getThumbnail() {
return mThumbnail;
}
/**
* Handles focus changes.
*/
void onFocusChanged(boolean focused) {
if (focused) {
if (Float.compare(getAlpha(), 1f) != 0) {
startFadeAnimation(1f, 0, 150, null);
}
} else {
if (Float.compare(getAlpha(), mConfig.taskViewThumbnailAlpha) != 0) {
startFadeAnimation(mConfig.taskViewThumbnailAlpha, 0, 150, null);
}
}
}
/**
* Prepares for the enter recents animation, this gets called before the the view
* is first visible and will be followed by a startEnterRecentsAnimation() call.
*/
void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask) {
if (isTaskViewLaunchTargetTask) {
mThumbnailAlpha = 1f;
} else {
mThumbnailAlpha = mConfig.taskViewThumbnailAlpha;
}
updateThumbnailPaintFilter();
}
/**
* Animates this task thumbnail as it enters Recents.
*/
void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) {
startFadeAnimation(mConfig.taskViewThumbnailAlpha, delay,
mConfig.taskViewEnterFromAppDuration, postAnimRunnable);
}
/**
* Animates this task thumbnail as it exits Recents.
*/
void startLaunchTaskAnimation(Runnable postAnimRunnable) {
startFadeAnimation(1f, 0, mConfig.taskViewExitToAppDuration, postAnimRunnable);
}
/**
* Starts a new thumbnail alpha animation.
*/
void startFadeAnimation(float finalAlpha, int delay, int duration, final Runnable postAnimRunnable) {
DVUtils.cancelAnimationWithoutCallbacks(mThumbnailAlphaAnimator);
mThumbnailAlphaAnimator = ValueAnimator.ofFloat(mThumbnailAlpha, finalAlpha);
mThumbnailAlphaAnimator.setStartDelay(delay);
mThumbnailAlphaAnimator.setDuration(duration);
mThumbnailAlphaAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
mThumbnailAlphaAnimator.addUpdateListener(mThumbnailAlphaUpdateListener);
if (postAnimRunnable != null) {
mThumbnailAlphaAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
postAnimRunnable.run();
}
});
}
mThumbnailAlphaAnimator.start();
}
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/views/DeckView.java
================================================
package com.appeaser.deckview.views;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import com.appeaser.deckview.R;
import com.appeaser.deckview.helpers.DeckChildViewTransform;
import com.appeaser.deckview.helpers.DeckViewConfig;
import com.appeaser.deckview.utilities.DVConstants;
import com.appeaser.deckview.utilities.DVUtils;
import com.appeaser.deckview.utilities.DozeTrigger;
import com.appeaser.deckview.utilities.ReferenceCountedTrigger;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
/**
* Created by Vikram on 02/04/2015.
*/
/* The visual representation of a task stack view */
public class DeckView<T> extends FrameLayout implements /*TaskStack.TaskStackCallbacks,*/
DeckChildView.DeckChildViewCallbacks<T>, DeckViewScroller.DeckViewScrollerCallbacks,
ViewPool.ViewPoolConsumer<DeckChildView<T>, T> {
DeckViewConfig mConfig;
DeckViewLayoutAlgorithm<T> mLayoutAlgorithm;
DeckViewScroller mStackScroller;
DeckViewTouchHandler mTouchHandler;
ViewPool<DeckChildView<T>, T> mViewPool;
ArrayList<DeckChildViewTransform> mCurrentTaskTransforms = new ArrayList<DeckChildViewTransform>();
DozeTrigger mUIDozeTrigger;
Rect mTaskStackBounds = new Rect();
int mFocusedTaskIndex = -1;
int mPrevAccessibilityFocusedIndex = -1;
// Optimizations
int mStackViewsAnimationDuration;
boolean mStackViewsDirty = true;
boolean mStackViewsClipDirty = true;
boolean mAwaitingFirstLayout = true;
boolean mStartEnterAnimationRequestedAfterLayout;
boolean mStartEnterAnimationCompleted;
ViewAnimation.TaskViewEnterContext mStartEnterAnimationContext;
int[] mTmpVisibleRange = new int[2];
float[] mTmpCoord = new float[2];
Matrix mTmpMatrix = new Matrix();
Rect mTmpRect = new Rect();
DeckChildViewTransform mTmpTransform = new DeckChildViewTransform();
HashMap<T, DeckChildView> mTmpTaskViewMap = new HashMap<T, DeckChildView>();
LayoutInflater mInflater;
// A convenience update listener to request updating clipping of tasks
ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
requestUpdateStackViewsClip();
}
};
public DeckView(Context context) {
this(context, null);
}
public DeckView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DeckView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public DeckView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
DeckViewConfig.reinitialize(getContext());
mConfig = DeckViewConfig.getInstance();
}
public void initialize(Callback<T> callback) {
mCallback = callback;
requestLayout();
mViewPool = new ViewPool<DeckChildView<T>, T>(getContext(), this);
mInflater = LayoutInflater.from(getContext());
mLayoutAlgorithm = new DeckViewLayoutAlgorithm<T>(mConfig);
mStackScroller = new DeckViewScroller(getContext(), mConfig, mLayoutAlgorithm);
mStackScroller.setCallbacks(this);
mTouchHandler = new DeckViewTouchHandler(getContext(), this, mConfig, mStackScroller);
mUIDozeTrigger = new DozeTrigger(mConfig.taskBarDismissDozeDelaySeconds, new Runnable() {
@Override
public void run() {
// Show the task bar dismiss buttons
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
DeckChildView tv = (DeckChildView) getChildAt(i);
tv.startNoUserInteractionAnimation();
}
}
});
}
/**
* Resets this TaskStackView for reuse.
*/
void reset() {
// Reset the focused task
resetFocusedTask();
// Return all the views to the pool
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
DeckChildView<T> tv = (DeckChildView) getChildAt(i);
mViewPool.returnViewToPool(tv);
}
// Mark each task view for relayout
if (mViewPool != null) {
Iterator<DeckChildView<T>> iter = mViewPool.poolViewIterator();
if (iter != null) {
while (iter.hasNext()) {
DeckChildView tv = iter.next();
tv.reset();
}
}
}
// Reset the stack state
mStackViewsDirty = true;
mStackViewsClipDirty = true;
mAwaitingFirstLayout = true;
mPrevAccessibilityFocusedIndex = -1;
if (mUIDozeTrigger != null) {
mUIDozeTrigger.stopDozing();
mUIDozeTrigger.resetTrigger();
}
mStackScroller.reset();
}
/**
* Requests that the views be synchronized with the model
*/
void requestSynchronizeStackViewsWithModel() {
requestSynchronizeStackViewsWithModel(0);
}
void requestSynchronizeStackViewsWithModel(int duration) {
if (!mStackViewsDirty) {
invalidate();
mStackViewsDirty = true;
}
if (mAwaitingFirstLayout) {
// Skip the animation if we are awaiting first layout
mStackViewsAnimationDuration = 0;
} else {
mStackViewsAnimationDuration = Math.max(mStackViewsAnimationDuration, duration);
}
}
/**
* Requests that the views clipping be updated.
*/
void requestUpdateStackViewsClip() {
if (!mStackViewsClipDirty) {
invalidate();
mStackViewsClipDirty = true;
}
}
/**
* Finds the child view given a specific task.
*/
public DeckChildView getChildViewForTask(T key) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
DeckChildView tv = (DeckChildView) getChildAt(i);
if (tv.getAttachedKey().equals(key)) {
return tv;
}
}
return null;
}
/**
* Returns the stack algorithm for this task stack.
*/
public DeckViewLayoutAlgorithm getStackAlgorithm() {
return mLayoutAlgorithm;
}
/**
* Gets the stack transforms of a list of tasks, and returns the visible range of tasks.
*/
private boolean updateStackTransforms(ArrayList<DeckChildViewTransform> taskTransforms,
ArrayList<T> data,
float stackScroll,
int[] visibleRangeOut,
boolean boundTranslationsToRect) {
int taskTransformCount = taskTransforms.size();
int taskCount = data.size();
int frontMostVisibleIndex = -1;
int backMostVisibleIndex = -1;
// We can reuse the task transforms where possible to reduce object allocation
if (taskTransformCount < taskCount) {
// If there are less transforms than tasks, then add as many transforms as necessary
for (int i = taskTransformCount; i < taskCount; i++) {
taskTransforms.add(new DeckChildViewTransform());
}
} else if (taskTransformCount > taskCount) {
// If there are more transforms than tasks, then just subset the transform list
taskTransforms.subList(0, taskCount);
}
// Update the stack transforms
DeckChildViewTransform prevTransform = null;
for (int i = taskCount - 1; i >= 0; i--) {
DeckChildViewTransform transform =
mLayoutAlgorithm.getStackTransform(data.get(i),
stackScroll, taskTransforms.get(i), prevTransform);
if (transform.visible) {
if (frontMostVisibleIndex < 0) {
frontMostVisibleIndex = i;
}
backMostVisibleIndex = i;
} else {
if (backMostVisibleIndex != -1) {
// We've reached the end of the visible range, so going down the rest of the
// stack, we can just reset the transforms accordingly
while (i >= 0) {
taskTransforms.get(i).reset();
i--;
}
break;
}
}
if (boundTranslationsToRect) {
transform.translationY = Math.min(transform.translationY,
mLayoutAlgorithm.mViewRect.bottom);
}
prevTransform = transform;
}
if (visibleRangeOut != null) {
visibleRangeOut[0] = frontMostVisibleIndex;
visibleRangeOut[1] = backMostVisibleIndex;
}
return frontMostVisibleIndex != -1 && backMostVisibleIndex != -1;
}
/**
* Synchronizes the views with the model
*/
boolean synchronizeStackViewsWithModel() {
if (mStackViewsDirty) {
// Get all the task transforms
ArrayList<T> data = mCallback.getData();
float stackScroll = mStackScroller.getStackScroll();
int[] visibleRange = mTmpVisibleRange;
boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms,
data, stackScroll, visibleRange, false);
// Return all the invisible children to the pool
mTmpTaskViewMap.clear();
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
DeckChildView<T> tv = (DeckChildView) getChildAt(i);
T key = tv.getAttachedKey();
int taskIndex = data.indexOf(key);
if (visibleRange[1] <= taskIndex
&& taskIndex <= visibleRange[0]) {
mTmpTaskViewMap.put(key, tv);
} else {
mViewPool.returnViewToPool(tv);
}
}
for (int i = visibleRange[0]; isValidVisibleRange && i >= visibleRange[1]; i--) {
T key = data.get(i);
DeckChildViewTransform transform = mCurrentTaskTransforms.get(i);
DeckChildView tv = mTmpTaskViewMap.get(key);
if (tv == null) {
// TODO Check
tv = mViewPool.pickUpViewFromPool(key, key);
if (mStackViewsAnimationDuration > 0) {
// For items in the list, put them in start animating them from the
// approriate ends of the list where they are expected to appear
if (Float.compare(transform.p, 0f) <= 0) {
mLayoutAlgorithm.getStackTransform(0f, 0f, mTmpTransform, null);
} else {
mLayoutAlgorithm.getStackTransform(1f, 0f, mTmpTransform, null);
}
tv.updateViewPropertiesToTaskTransform(mTmpTransform, 0);
}
}
// Animate the task into place
tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(i),
mStackViewsAnimationDuration, mRequestUpdateClippingListener);
}
// Reset the request-synchronize params
mStackViewsAnimationDuration = 0;
mStackViewsDirty = false;
mStackViewsClipDirty = true;
return true;
}
return false;
}
/**
* Updates the clip for each of the task views.
*/
void clipTaskViews() {
// Update the clip on each task child
if (DVConstants.DebugFlags.App.EnableTaskStackClipping) {
int childCount = getChildCount();
for (int i = 0; i < childCount - 1; i++) {
DeckChildView tv = (DeckChildView) getChildAt(i);
DeckChildView nextTv = null;
DeckChildView tmpTv = null;
int clipBottom = 0;
if (tv.shouldClipViewInStack()) {
// Find the next view to clip against
int nextIndex = i;
while (nextIndex < getChildCount()) {
tmpTv = (DeckChildView) getChildAt(++nextIndex);
if (tmpTv != null && tmpTv.shouldClipViewInStack()) {
nextTv = tmpTv;
break;
}
}
// Clip against the next view, this is just an approximation since we are
// stacked and we can make assumptions about the visibility of the this
// task relative to the ones in front of it.
if (nextTv != null) {
// Map the top edge of next task view into the local space of the current
// task view to find the clip amount in local space
mTmpCoord[0] = mTmpCoord[1] = 0;
DVUtils.mapCoordInDescendentToSelf(nextTv, this, mTmpCoord, false);
DVUtils.mapCoordInSelfToDescendent(tv, this, mTmpCoord, mTmpMatrix);
clipBottom = (int) Math.floor(tv.getMeasuredHeight() - mTmpCoord[1]
- nextTv.getPaddingTop() - 1);
}
}
tv.getViewBounds().setClipBottom(clipBottom);
}
if (getChildCount() > 0) {
// The front most task should never be clipped
DeckChildView tv = (DeckChildView) getChildAt(getChildCount() - 1);
tv.getViewBounds().setClipBottom(0);
}
}
mStackViewsClipDirty = false;
}
/**
* The stack insets to apply to the stack contents
*/
public void setStackInsetRect(Rect r) {
mTaskStackBounds.set(r);
}
/**
* Updates the min and max virtual scroll bounds
*/
void updateMinMaxScroll(boolean boundScrollToNewMinMax, boolean launchedWithAltTab,
boolean launchedFromHome) {
// Compute the min and max scroll values
mLayoutAlgorithm.computeMinMaxScroll(mCallback.getData(), launchedWithAltTab, launchedFromHome);
// Debug logging
if (boundScrollToNewMinMax) {
mStackScroller.boundScroll();
}
}
/**
* Returns the scroller.
*/
public DeckViewScroller getScroller() {
return mStackScroller;
}
/**
* Focuses the task at the specified index in the stack
*/
void focusTask(int childIndex, boolean scrollToNewPosition, final boolean animateFocusedState) {
// Return early if the task is already focused
if (childIndex == mFocusedTaskIndex) return;
ArrayList<T> data = mCallback.getData();
if (0 <= childIndex && childIndex < data.size()) {
mFocusedTaskIndex = childIndex;
// Focus the view if possible, otherwise, focus the view after we scroll into position
T key = data.get(childIndex);
DeckChildView tv = getChildViewForTask(key);
Runnable postScrollRunnable = null;
if (tv != null) {
tv.setFocusedTask(animateFocusedState);
} else {
postScrollRunnable = new Runnable() {
@Override
public void run() {
DeckChildView tv = getChildViewForTask(mCallback.getData().get(mFocusedTaskIndex));
if (tv != null) {
tv.setFocusedTask(animateFocusedState);
}
}
};
}
// Scroll the view into position (just center it in the curve)
if (scrollToNewPosition) {
float newScroll = mLayoutAlgorithm.getStackScrollForTask(key) - 0.5f;
newScroll = mStackScroller.getBoundedStackScroll(newScroll);
mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll, postScrollRunnable);
} else {
if (postScrollRunnable != null) {
postScrollRunnable.run();
}
}
}
}
/**
* Ensures that there is a task focused, if nothing is focused, then we will use the task
* at the center of the visible stack.
*/
public boolean ensureFocusedTask() {
if (mFocusedTaskIndex < 0) {
// If there is no task focused, then find the task that is closes to the center
// of the screen and use that as the currently focused task
int x = mLayoutAlgorithm.mStackVisibleRect.centerX();
int y = mLayoutAlgorithm.mStackVisibleRect.centerY();
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
DeckChildView tv = (DeckChildView) getChildAt(i);
tv.getHitRect(mTmpRect);
if (mTmpRect.contains(x, y)) {
mFocusedTaskIndex = i;
break;
}
}
// If we can't find the center task, then use the front most index
if (mFocusedTaskIndex < 0 && childCount > 0) {
mFocusedTaskIndex = childCount - 1;
}
}
return mFocusedTaskIndex >= 0;
}
/**
* Focuses the next task in the stack.
*
* @param animateFocusedState determines whether to actually draw the highlight along with
* the change in focus, as well as whether to scroll to fit the
* task into view.
*/
public void focusNextTask(boolean forward, boolean animateFocusedState) {
// Find the next index to focus
int numTasks = mCallback.getData().size();
if (numTasks == 0) return;
int direction = (forward ? -1 : 1);
int newIndex = mFocusedTaskIndex + direction;
if (newIndex >= 0 && newIndex <= (numTasks - 1)) {
newIndex = Math.max(0, Math.min(numTasks - 1, newIndex));
focusTask(newIndex, true, animateFocusedState);
}
}
/**
* Dismisses the focused task.
*/
public void dismissFocusedTask() {
// Return early if the focused task index is invalid
if (mFocusedTaskIndex < 0 || mFocusedTaskIndex >= mCallback.getData().size()) {
mFocusedTaskIndex = -1;
return;
}
//Long id = mAdapter.getItemId(mFocusedTaskIndex);
T key = mCallback.getData().get(mFocusedTaskIndex);
DeckChildView tv = getChildViewForTask(key);
tv.dismissTask();
}
/**
* Resets the focused task.
*/
void resetFocusedTask() {
if ((0 <= mFocusedTaskIndex) && (mFocusedTaskIndex < mCallback.getData().size())) {
DeckChildView tv = getChildViewForTask(mCallback.getData().get(mFocusedTaskIndex));
if (tv != null) {
tv.unsetFocusedTask();
}
}
mFocusedTaskIndex = -1;
}
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
int childCount = getChildCount();
if (childCount > 0) {
DeckChildView<T> backMostTask = (DeckChildView) getChildAt(0);
DeckChildView<T> frontMostTask = (DeckChildView) getChildAt(childCount - 1);
event.setFromIndex(mCallback.getData().indexOf(backMostTask.getAttachedKey()));
event.setToIndex(mCallback.getData().indexOf(frontMostTask.getAttachedKey()));
}
event.setItemCount(mCallback.getData().size());
event.setScrollY(mStackScroller.mScroller.getCurrY());
event.setMaxScrollY(mStackScroller.progressToScrollRange(mLayoutAlgorithm.mMaxScrollP));
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mTouchHandler.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return mTouchHandler.onTouchEvent(ev);
}
@Override
public boolean onGenericMotionEvent(MotionEvent ev) {
return mTouchHandler.onGenericMotionEvent(ev);
}
@Override
public void computeScroll() {
mStackScroller.computeScroll();
// Synchronize the views
synchronizeStackViewsWithModel();
clipTaskViews();
// Notify accessibility
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
}
/**
* Computes the stack and task rects
*/
public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds,
boolean launchedWithAltTab, boolean launchedFromHome) {
// Compute the rects in the stack algorithm
mLayoutAlgorithm.computeRects(windowWidth, windowHeight, taskStackBounds);
// Update the scroll bounds
updateMinMaxScroll(false, launchedWithAltTab, launchedFromHome);
}
public int getCurrentChildIndex() {
if (getChildCount() == 0)
return -1;
DeckChildView<T> frontMostChild = (DeckChildView) getChildAt(getChildCount() / 2);
if (frontMostChild != null) {
return mCallback.getData().indexOf(frontMostChild.getAttachedKey());
}
return -1;
}
/**
* Focuses the task at the specified index in the stack
*/
public void scrollToChild(int childIndex) {
if (getCurrentChildIndex() == childIndex)
return;
if (0 <= childIndex && childIndex < mCallback.getData().size()) {
// Scroll the view into position (just center it in the curve)
float newScroll = mLayoutAlgorithm.getStackScrollForTask(
mCallback.getData().get(childIndex)) - 0.5f;
newScroll = mStackScroller.getBoundedStackScroll(newScroll);
mStackScroller.setStackScroll(newScroll);
//Alternate (animated) way
//mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll, null);
}
}
/**
* Computes the maximum number of visible tasks and thumbnails. Requires that
* updateMinMaxScrollForStack() is called first.
*/
public DeckViewLayoutAlgorithm.VisibilityReport computeStackVisibilityReport() {
return mLayoutAlgorithm.computeStackVisibilityReport(mCallback.getData());
}
/**
* This is called with the full window width and height to allow stack view children to
* perform the full screen transition down.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
Rect _taskStackBounds = new Rect();
mConfig.getTaskStackBounds(width, height, mConfig.systemInsets.top,
mConfig.systemInsets.right, _taskStackBounds);
setStackInsetRect(_taskStackBounds);
// Compute our stack/task rects
Rect taskStackBounds = new Rect(mTaskStackBounds);
taskStackBounds.bottom -= mConfig.systemInsets.bottom;
computeRects(width, height, taskStackBounds, mConfig.launchedWithAltTab,
mConfig.launchedFromHome);
// If this is the first layout, then scroll to the front of the stack and synchronize the
// stack views immediately to load all the views
if (mAwaitingFirstLayout) {
mStackScroller.setStackScrollToInitialState();
requestSynchronizeStackViewsWithModel();
synchronizeStackViewsWithModel();
}
// Measure each of the TaskViews
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
DeckChildView tv = (DeckChildView) getChildAt(i);
if (tv.getBackground() != null) {
tv.getBackground().getPadding(mTmpRect);
} else {
mTmpRect.setEmpty();
}
tv.measure(
MeasureSpec.makeMeasureSpec(
mLayoutAlgorithm.mTaskRect.width() + mTmpRect.left + mTmpRect.right,
MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(
mLayoutAlgorithm.mTaskRect.height() + mTmpRect.top + mTmpRect.bottom,
MeasureSpec.EXACTLY));
}
setMeasuredDimension(width, height);
}
/**
* This is called with the size of the space not including the top or right insets, or the
* search bar height in portrait (but including the search bar width in landscape, since we want
* to draw under it.
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// Layout each of the children
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
DeckChildView tv = (DeckChildView) getChildAt(i);
if (tv.getBackground() != null) {
tv.getBackground().getPadding(mTmpRect);
} else {
mTmpRect.setEmpty();
}
tv.layout(mLayoutAlgorithm.mTaskRect.left - mTmpRect.left,
mLayoutAlgorithm.mTaskRect.top - mTmpRect.top,
mLayoutAlgorithm.mTaskRect.right + mTmpRect.right,
mLayoutAlgorithm.mTaskRect.bottom + mTmpRect.bottom);
}
if (mAwaitingFirstLayout) {
mAwaitingFirstLayout = false;
onFirstLayout();
}
}
/**
* Handler for the first layout.
*/
void onFirstLayout() {
int offscreenY = mLayoutAlgorithm.mViewRect.bottom -
(mLayoutAlgorithm.mTaskRect.top - mLayoutAlgorithm.mViewRect.top);
int childCount = getChildCount();
// Prepare the first view for its enter animation
for (int i = childCount - 1; i >= 0; i--) {
DeckChildView tv = (DeckChildView) getChildAt(i);
// TODO: The false needs to go!
tv.prepareEnterRecentsAnimation(i == childCount - 1, false, offscreenY);
}
// If the enter animation started already and we haven't completed a layout yet, do the
// enter animation now
if (mStartEnterAnimationRequestedAfterLayout) {
startEnterRecentsAnimation(mStartEnterAnimationContext);
mStartEnterAnimationRequestedAfterLayout = false;
mStartEnterAnimationContext = null;
}
// When Alt-Tabbing, focus the previous task (but leave the animation until we finish the
// enter animation).
if (mConfig.launchedWithAltTab) {
if (mConfig.launchedFromAppWithThumbnail) {
focusTask(Math.max(0, mCallback.getData().size() - 2), false,
mConfig.launchedHasConfigurationChanged);
} else {
focusTask(Math.max(0, mCallback.getData().size() - 1), false,
mConfig.launchedHasConfigurationChanged);
}
}
// Start dozing
mUIDozeTrigger.startDozing();
}
void showDeck(Context context) {
// Try and start the enter animation (or restart it on configuration changed)
ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null);
ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
// We have to increment/decrement the post animation trigger in case there are no children
// to ensure that it runs
ctx.postAnimationTrigger.increment();
startEnterRecentsAnimation(ctx);
ctx.postAnimationTrigger.decrement();
}
/**
* Requests this task stacks to start it's enter-recents animation
*/
public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
// If we are still waiting to layout, then just defer until then
if (mAwaitingFirstLayout) {
mStartEnterAnimationRequestedAfterLayout = true;
mStartEnterAnimationContext = ctx;
return;
}
if (mCallback.getData().size() > 0) {
int childCount = getChildCount();
// Animate all the task views into view
for (int i = childCount - 1; i >= 0; i--) {
DeckChildView<T> tv = (DeckChildView) getChildAt(i);
T key = tv.getAttachedKey();
ctx.currentTaskTransform = new DeckChildViewTransform();
ctx.currentStackViewIndex = i;
ctx.currentStackViewCount = childCount;
ctx.currentTaskRect = mLayoutAlgorithm.mTaskRect;
// TODO: this needs to go
ctx.currentTaskOccludesLaunchTarget = false;
ctx.updateListener = mRequestUpdateClippingListener;
mLayoutAlgorithm.getStackTransform(key, mStackScroller.getStackScroll(),
ctx.currentTaskTransform, null);
tv.startEnterRecentsAnimation(ctx);
}
// Add a runnable to the post animation ref counter to clear all the views
ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
mStartEnterAnimationCompleted = true;
// Poke the dozer to restart the trigger after the animation completes
mUIDozeTrigger.poke();
}
});
}
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
// Update the configuration with the latest system insets and trigger a relayout
// mConfig.updateSystemInsets(insets.getSystemWindowInsets());
mConfig.updateSystemInsets(new Rect(insets.getSystemWindowInsetLeft(),
insets.getSystemWindowInsetTop(),
insets.getSystemWindowInsetRight(),
insets.getSystemWindowInsetBottom()));
requestLayout();
return insets.consumeSystemWindowInsets();
}
void hideDeck(Context context, Runnable finishRunnable) {
ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(context,
null, finishRunnable, null);
ViewAnimation.TaskViewExitContext exitCtx =
new ViewAnimation.TaskViewExitContext(exitTrigger);
exitCtx.postAnimationTrigger.increment();
startExitToHomeAnimation(
new ViewAnimation.TaskViewExitContext(exitTrigger));
exitCtx.postAnimationTrigger.decrement();
}
/**
* Requests this task stacks to start it's exit-recents animation.
*/
public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
// Stop any scrolling
mStackScroller.stopScroller();
mStackScroller.stopBoundScrollAnimation();
// Animate all the task views out of view
ctx.offscreenTranslationY = mLayoutAlgorithm.mViewRect.bottom -
(mLayoutAlgorithm.mTaskRect.top - mLayoutAlgorithm.mViewRect.top);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
DeckChildView tv = (DeckChildView) getChildAt(i);
tv.startExitToHomeAnimation(ctx);
}
}
/**
* Animates a task view in this stack as it launches.
*/
public void startLaunchTaskAnimation(DeckChildView tv, Runnable r, boolean lockToTask) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
DeckChildView t = (DeckChildView) getChildAt(i);
if (t == tv) {
t.setClipViewInStack(false);
t.startLaunchTaskAnimation(r, true, true, lockToTask);
} else {
// TODO: the false needs to go
t.startLaunchTaskAnimation(null, false, false, lockToTask);
}
}
}
/**
* Final callback after Recents is finally hidden.
*/
void onRecentsHidden() {
reset();
}
public boolean isTransformedTouchPointInView(float x, float y, View child) {
// TODO: confirm if this is the right approach
if (child == null)
return false;
final Rect frame = new Rect();
child.getHitRect(frame);
return frame.contains((int) x, (int) y);
}
/**
* Pokes the dozer on user interaction.
*/
void onUserInteraction() {
// Poke the doze trigger if it is dozing
mUIDozeTrigger.poke();
}
/**
* * ViewPoolConsumer Implementation ***
*/
@Override
public DeckChildView createView(Context context) {
return (DeckChildView) mInflater.inflate(R.layout.deck_child_view, this, false);
}
@Override
public void prepareViewToEnterPool(DeckChildView<T> tv) {
T key = tv.getAttachedKey();
mCallback.unloadViewData(key);
tv.onTaskUnbound();
tv.onDataUnloaded();
// Detach the view from the hierarchy
detachViewFromParent(tv);
// Reset the view properties
tv.resetViewProperties();
// Reset the clip state of the task view
tv.setClipViewInStack(false);
}
@Override
public void prepareViewToLeavePool(DeckChildView<T> dcv, T key, boolean isNewView) {
// It is possible for a view to be returned to the view pool before it is laid out,
// which means that we will need to relayout the view when it is first used next.
boolean requiresRelayout = dcv.getWidth() <= 0 && !isNewView;
// Rebind the task and request that this task's data be filled into the TaskView
dcv.onTaskBound(key);
// Load the task data
mCallback.loadViewData(new WeakReference<DeckChildView<T>>(dcv), key);
// If the doze trigger has already fired, then update the state for this task view
if (mUIDozeTrigger.hasTriggered()) {
dcv.setNoUserInteractionState();
}
// If we've finished the start animation, then ensure we always enable the focus animations
if (mStartEnterAnimationCompleted) {
dcv.enableFocusAnimations();
}
// Find the index where this task should be placed in the stack
int insertIndex = -1;
int position = mCallback.getData().indexOf(key);
if (position != -1) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
T otherKey = ((DeckChildView<T>) getChildAt(i)).getAttachedKey();
int pos = mCallback.getData().indexOf(otherKey);
if (position < pos) {
insertIndex = i;
break;
}
}
}
// Add/attach the view to the hierarchy
if (isNewView) {
addView(dcv, insertIndex);
} else {
attachViewToParent(dcv, insertIndex, dcv.getLayoutParams());
if (requiresRelayout) {
dcv.requestLayout();
}
}
// Set the new state for this view, including the callbacks and view clipping
dcv.setCallbacks(this);
dcv.setTouchEnabled(true);
dcv.setClipViewInStack(true);
}
@Override
public boolean hasPreferredData(DeckChildView<T> tv, T preferredData) {
return (tv.getAttachedKey() != null && tv.getAttachedKey().equals(preferredData));
}
/**
* * DeckChildCallbacks Implementation ***
*/
@Override
public void onDeckChildViewAppIconClicked(DeckChildView tv) {
//
}
@Override
public void onDeckChildViewAppInfoClicked(DeckChildView tv) {
//
}
@Override
public void onDeckChildViewClicked(DeckChildView<T> dcv, T key) {
// Cancel any doze triggers
mUIDozeTrigger.stopDozing();
mCallback.onItemClick(key);
}
@Override
public void onDeckChildViewDismissed(DeckChildView<T> dcv) {
boolean taskWasFocused = dcv.isFocusedTask();
T key = dcv.getAttachedKey();
int taskIndex = mCallback.getData().indexOf(key);
onStackTaskRemoved(dcv);
// If the dismissed task was focused, then we should focus the new task in the same index
if (taskIndex != -1 && taskWasFocused) {
int nextTaskIndex = Math.min(mCallback.getData().size() - 1, taskIndex - 1);
if (nextTaskIndex >= 0) {
DeckChildView nextTv = getChildViewForTask(mCallback.getData().get(nextTaskIndex));
if (nextTv != null) {
// Focus the next task, and only animate the visible state if we are launched
// from Alt-Tab
nextTv.setFocusedTask(mConfig.launchedWithAltTab);
}
}
}
}
public void onStackTaskRemoved(DeckChildView<T> removedView) {
// Remove the view associated with this task, we can't rely on updateTransforms
// to work here because the task is no longer in the list
if (removedView != null) {
T key = removedView.getAttachedKey();
int removedPosition = mCallback.getData().indexOf(key);
mViewPool.returnViewToPool(removedView);
// Notify the callback that we've removed the task and it can clean up after it
mCallback.onViewDismissed(key);
}
/*
// Get the stack scroll of the task to anchor to (since we are removing something, the front
// most task will be our anchor task)
T anchorTask = null;
float prevAnchorTaskScroll = 0;
boolean pullStackForward = mCallback.getData().size() > 0;
if (pullStackForward) {
anchorTask = mCallback.getData().get(mCallback.getData().size() - 1);
prevAnchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
}
// Update the min/max scroll and animate other task views into their new positions
updateMinMaxScroll(true, mConfig.launchedWithAltTab, mConfig.launchedFromHome);
// Offset the stack by as much as the anchor task would otherwise move back
if (pullStackForward) {
float anchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
mStackScroller.setStackScroll(mStackScroller.getStackScroll() + (anchorTaskScroll
- prevAnchorTaskScroll));
mStackScroller.boundScroll();
}
// Animate all the tasks into place
requestSynchronizeStackViewsWithModel(200);
T newFrontMostTask = mCallback.getData().get(mCallback.getData().size() - 1);
// Update the new front most task
if (newFrontMostTask != null) {
DeckChildView<T> frontTv = getChildViewForTask(newFrontMostTask);
if (frontTv != null) {
frontTv.onTaskBound(newFrontMostTask);
}
}
// If there are no remaining tasks
if (mCallback.getData().size() == 0) {
mCallback.onNoViewsToDeck();
}
*/
}
public void notifyDataSetChanged() {
// Get the stack scroll of the task to anchor to (since we are removing something, the front
// most task will be our anchor task)
T anchorTask = null;
float prevAnchorTaskScroll = 0;
boolean pullStackForward = mCallback.getData().size() > 0;
if (pullStackForward) {
anchorTask = mCallback.getData().get(mCallback.getData().size() - 1);
prevAnchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
}
// Update the min/max scroll and animate other task views into their new positions
updateMinMaxScroll(true, mConfig.launchedWithAltTab, mConfig.launchedFromHome);
// Offset the stack by as much as the anchor task would otherwise move back
if (pullStackForward) {
float anchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
mStackScroller.setStackScroll(mStackScroller.getStackScroll() + (anchorTaskScroll
- prevAnchorTaskScroll));
mStackScroller.boundScroll();
}
// Animate all the tasks into place
requestSynchronizeStackViewsWithModel(200);
T newFrontMostTask = mCallback.getData().size() > 0 ?
mCallback.getData().get(mCallback.getData().size() - 1)
: null;
// Update the new front most task
if (newFrontMostTask != null) {
DeckChildView<T> frontTv = getChildViewForTask(newFrontMostTask);
if (frontTv != null) {
frontTv.onTaskBound(newFrontMostTask);
}
}
// If there are no remaining tasks
if (mCallback.getData().size() == 0) {
mCallback.onNoViewsToDeck();
}
}
@Override
public void onDeckChildViewClipStateChanged(DeckChildView tv) {
if (!mStackViewsDirty) {
invalidate();
}
}
@Override
public void onDeckChildViewFocusChanged(DeckChildView<T> tv, boolean focused) {
if (focused) {
mFocusedTaskIndex = mCallback.getData().indexOf(tv.getAttachedKey());
}
}
/**
* * TaskStackViewScroller.TaskStackViewScrollerCallbacks ***
*/
@Override
public void onScrollChanged(float p) {
mUIDozeTrigger.poke();
requestSynchronizeStackViewsWithModel();
postInvalidateOnAnimation();
}
public void notifyDataSetChangedOld() {
ArrayList<T> data = mCallback.getData();
// Get the stack scroll of the task to anchor to (since we are removing something, the front
// most task will be our anchor task)
T anchorTask = null;
float prevAnchorTaskScroll = 0;
boolean pullStackForward = data.size() > 0;
if (pullStackForward) {
anchorTask = data.get(data.size() - 1);
prevAnchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
}
// Update the min/max scroll and animate other task views into their new positions
updateMinMaxScroll(true, mConfig.launchedWithAltTab, mConfig.launchedFromHome);
// Offset the stack by as much as the anchor task would otherwise move back
if (pullStackForward) {
float anchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
mStackScroller.setStackScroll(mStackScroller.getStackScroll() + (anchorTaskScroll
- prevAnchorTaskScroll));
mStackScroller.boundScroll();
}
// Animate all the tasks into place
requestSynchronizeStackViewsWithModel(200);
T newFrontMostTask = data.get(data.size() - 1);
// Update the new front most task
if (newFrontMostTask != null) {
DeckChildView<T> frontTv = getChildViewForTask(newFrontMostTask);
if (frontTv != null) {
frontTv.onTaskBound(newFrontMostTask);
}
}
// If there are no remaining tasks
if (mCallback.getData().size() == 0)
mCallback.onNoViewsToDeck();
}
Callback<T> mCallback;
public interface Callback<T> {
public ArrayList<T> getData();
public void loadViewData(WeakReference<DeckChildView<T>> dcv, T item);
public void unloadViewData(T item);
public void onViewDismissed(T item);
public void onItemClick(T item);
public void onNoViewsToDeck();
}
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/views/DeckViewLayoutAlgorithm.java
================================================
package com.appeaser.deckview.views;
import android.graphics.Rect;
import com.appeaser.deckview.helpers.DeckChildViewTransform;
import com.appeaser.deckview.helpers.DeckViewConfig;
import com.appeaser.deckview.utilities.DVUtils;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Created by Vikram on 02/04/2015.
*/
/* The layout logic for a TaskStackView.
*
* We are using a curve that defines the curve of the tasks as that go back in the recents list.
* The curve is defined such that at curve progress p = 0 is the end of the curve (the top of the
* stack rect), and p = 1 at the start of the curve and the bottom of the stack rect.
*/
public class DeckViewLayoutAlgorithm<T> {
// These are all going to change
static final float StackPeekMinScale = 0.8f; // The min scale of the last card in the peek area
// A report of the visibility state of the stack
public class VisibilityReport {
public int numVisibleTasks;
public int numVisibleThumbnails;
/**
* Package level ctor
*/
VisibilityReport(int tasks, int thumbnails) {
numVisibleTasks = tasks;
numVisibleThumbnails = thumbnails;
}
}
DeckViewConfig mConfig;
// The various rects that define the stack view
public Rect mViewRect = new Rect();
Rect mStackVisibleRect = new Rect();
Rect mStackRect = new Rect();
Rect mTaskRect = new Rect();
// The min/max scroll progress
float mMinScrollP;
float mMaxScrollP;
float mInitialScrollP;
int mWithinAffiliationOffset;
int mBetweenAffiliationOffset;
HashMap<T, Float> mTaskProgressMap = new HashMap<T, Float>();
// Log function
static final float XScale = 1.75f; // The large the XScale, the longer the flat area of the curve
static final float LogBase = 3000;
static final int PrecisionSteps = 250;
static float[] xp;
static float[] px;
public DeckViewLayoutAlgorithm(DeckViewConfig config) {
mConfig = config;
// Precompute the path
initializeCurve();
}
/**
* Computes the stack and task rects
*/
public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
// Compute the stack rects
mViewRect.set(0, 0, windowWidth, windowHeight);
mStackRect.set(taskStackBounds);
mStackVisibleRect.set(taskStackBounds);
mStackVisibleRect.bottom = mViewRect.bottom;
int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
int heightPadding = mConfig.taskStackTopPaddingPx;
mStackRect.inset(widthPadding, heightPadding);
// Compute the task rect
int size = mStackRect.width();
int left = mStackRect.left + (mStackRect.width() - size) / 2;
mTaskRect.set(left, mStackRect.top,
left + size, mStackRect.top + size);
// Update the affiliation offsets
float visibleTaskPct = 0.5f;
mWithinAffiliationOffset = mConfig.taskBarHeight;
mBetweenAffiliationOffset = (int) (visibleTaskPct * mTaskRect.height());
}
/**
* Computes the minimum and maximum scroll progress values. This method may be called before
* the RecentsConfiguration is set, so we need to pass in the alt-tab state.
*/
void computeMinMaxScroll(ArrayList<T> data, boolean launchedWithAltTab,
boolean launchedFromHome) {
// Clear the progress map
mTaskProgressMap.clear();
// Return early if we have no tasks
if (data.isEmpty()) {
mMinScrollP = mMaxScrollP = 0;
return;
}
// Note that we should account for the scale difference of the offsets at the screen bottom
int taskHeight = mTaskRect.height();
float pAtBottomOfStackRect = screenYToCurveProgress(mStackVisibleRect.bottom);
float pWithinAffiliateTop = screenYToCurveProgress(mStackVisibleRect.bottom -
mWithinAffiliationOffset);
float scale = curveProgressToScale(pWithinAffiliateTop);
int scaleYOffset = (int) (((1f - scale) * taskHeight) / 2);
pWithinAffiliateTop = screenYToCurveProgress(mStackVisibleRect.bottom -
mWithinAffiliationOffset + scaleYOffset);
float pWithinAffiliateOffset = pAtBottomOfStackRect - pWithinAffiliateTop;
float pBetweenAffiliateOffset = pAtBottomOfStackRect -
screenYToCurveProgress(mStackVisibleRect.bottom - mBetweenAffiliationOffset);
float pTaskHeightOffset = pAtBottomOfStackRect -
screenYToCurveProgress(mStackVisibleRect.bottom - taskHeight);
float pNavBarOffset = pAtBottomOfStackRect -
screenYToCurveProgress(mStackVisibleRect.bottom - (mStackVisibleRect.bottom -
mStackRect.bottom));
// Update the task offsets
float pAtBackMostCardTop = 0.5f;
float pAtFrontMostCardTop = pAtBackMostCardTop;
int taskCount = data.size();
for (int i = 0; i < taskCount; i++) {
//Task task = tasks.get(i);
//mTaskProgressMap.put(task.key, pAtFrontMostCardTop);
mTaskProgressMap.put(data.get(i), pAtFrontMostCardTop);
if (i < (taskCount - 1)) {
// Increment the peek height
// TODO: Might need adjustments
//float pPeek = task.group.isFrontMostTask(task) ?
//pBetweenAffiliateOffset : pWithinAffiliateOffset;
float pPeek = pBetweenAffiliateOffset;
pAtFrontMostCardTop += pPeek;
}
}
mMaxScrollP = pAtFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
mMinScrollP = data.size() == 1 ? Math.max(mMaxScrollP, 0f) : 0f;
if (launchedWithAltTab && launchedFromHome) {
// Center the top most task, since that will be focused first
mInitialScrollP = mMaxScrollP;
} else {
mInitialScrollP = pAtFrontMostCardTop - 0.825f;
}
mInitialScrollP = Math.min(mMaxScrollP, Math.max(0, mInitialScrollP));
}
/**
* Computes the maximum number of visible tasks and thumbnails. Requires that
* computeMinMaxScroll() is called first.
*/
public VisibilityReport computeStackVisibilityReport(ArrayList<T> data) {
if (data.size() <= 1) {
return new VisibilityReport(1, 1);
}
// Walk backwards in the task stack and count the number of tasks and visible thumbnails
int taskHeight = mTaskRect.height();
int numVisibleTasks = 1;
int numVisibleThumbnails = 1;
//float progress = mTaskProgressMap.get(tasks.get(tasks.size() - 1).key) - mInitialScrollP;
float progress = mTaskProgressMap.get(data.get(data.size() - 1)) - mInitialScrollP;
int prevScreenY = curveProgressToScreenY(progress);
for (int i = data.size() - 2; i >= 0; i--) {
//Task task = tasks.get(i);
//progress = mTaskProgressMap.get(task.key) - mInitialScrollP;
progress = mTaskProgressMap.get(data.get(i)) - mInitialScrollP;
if (progress < 0) {
break;
}
// TODO: Might need adjustments
//boolean isFrontMostTaskInGroup = task.group.isFrontMostTask(task);
boolean isFrontMostTaskInGroup = true;
if (isFrontMostTaskInGroup) {
float scaleAtP = curveProgressToScale(progress);
int scaleYOffsetAtP = (int) (((1f - scaleAtP) * taskHeight) / 2);
int screenY = curveProgressToScreenY(progress) + scaleYOffsetAtP;
boolean hasVisibleThumbnail = (prevScreenY - screenY) > mConfig.taskBarHeight;
if (hasVisibleThumbnail) {
numVisibleThumbnails++;
numVisibleTasks++;
prevScreenY = screenY;
} else {
// Once we hit the next front most task that does not have a visible thumbnail,
// walk through remaining visible set
for (int j = i; j >= 0; j--) {
numVisibleTasks++;
progress = mTaskProgressMap.get(data.get(i)) - mInitialScrollP;
if (progress < 0) {
break;
}
}
break;
}
} else if (!isFrontMostTaskInGroup) {
// Affiliated task, no thumbnail
numVisibleTasks++;
}
}
return new VisibilityReport(numVisibleTasks, numVisibleThumbnails);
}
/**
* Update/get the transform
*/
public DeckChildViewTransform getStackTransform(T key, float stackScroll,
DeckChildViewTransform transformOut,
DeckChildViewTransform prevTransform) {
// Return early if we have an invalid index
if (!mTaskProgressMap.containsKey(key)) {
transformOut.reset();
return transformOut;
}
return getStackTransform(mTaskProgressMap.get(key), stackScroll, transformOut,
prevTransform);
}
/**
* Update/get the transform
*/
public DeckChildViewTransform getStackTransform(float taskProgress, float stackScroll,
DeckChildViewTransform transformOut,
DeckChildViewTransform prevTransform) {
float pTaskRelative = taskProgress - stackScroll;
float pBounded = Math.max(0, Math.min(pTaskRelative, 1f));
// If the task top is outside of the bounds below the screen, then immediately reset it
if (pTaskRelative > 1f) {
transformOut.reset();
transformOut.rect.set(mTaskRect);
return transformOut;
}
// The check for the top is trickier, since we want to show the next task if it is at all
// visible, even if p < 0.
if (pTaskRelative < 0f) {
if (prevTransform != null && Float.compare(prevTransform.p, 0f) <= 0) {
transformOut.reset();
transformOut.rect.set(mTaskRect);
return transformOut;
}
}
float scale = curveProgressToScale(pBounded);
int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
int minZ = mConfig.taskViewTranslationZMinPx;
int maxZ = mConfig.taskViewTranslationZMaxPx;
transformOut.scale = scale;
transformOut.translationY = curveProgressToScreenY(pBounded) - mStackVisibleRect.top -
scaleYOffset;
transformOut.translationZ = Math.max(minZ, minZ + (pBounded * (maxZ - minZ)));
transformOut.rect.set(mTaskRect);
transformOut.rect.offset(0, transformOut.translationY);
DVUtils.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
transformOut.visible = true;
transformOut.p = pTaskRelative;
return transformOut;
}
/**
* Returns the untransformed task view size.
*/
public Rect getUntransformedTaskViewSize() {
Rect tvSize = new Rect(mTaskRect);
tvSize.offsetTo(0, 0);
return tvSize;
}
/**
* Returns the scroll to such task top = 1f;
*/
public float getStackScrollForTask(T key) {
if (!mTaskProgressMap.containsKey(key)) return 0f;
return mTaskProgressMap.get(key);
}
/**
* Initializes the curve.
*/
public static void initializeCurve() {
if (xp != null && px != null) return;
xp = new float[PrecisionSteps + 1];
px = new float[PrecisionSteps + 1];
// Approximate f(x)
float[] fx = new float[PrecisionSteps + 1];
float step = 1f / PrecisionSteps;
float x = 0;
for (int xStep = 0; xStep <= PrecisionSteps; xStep++) {
fx[xStep] = logFunc(x);
x += step;
}
// Calculate the arc length for x:1->0
float pLength = 0;
float[] dx = new float[PrecisionSteps + 1];
dx[0] = 0;
for (int xStep = 1; xStep < PrecisionSteps; xStep++) {
dx[xStep] = (float) Math.sqrt(Math.pow(fx[xStep] - fx[xStep - 1], 2) + Math.pow(step, 2));
pLength += dx[xStep];
}
// Approximate p(x), a function of cumulative progress with x, normalized to 0..1
float p = 0;
px[0] = 0f;
px[PrecisionSteps] = 1f;
for (int xStep = 1; xStep <= PrecisionSteps; xStep++) {
p += Math.abs(dx[xStep] / pLength);
px[xStep] = p;
}
// Given p(x), calculate the inverse function x(p). This assumes that x(p) is also a valid
// function.
int xStep = 0;
p = 0;
xp[0] = 0f;
xp[PrecisionSteps] = 1f;
for (int pStep = 0; pStep < PrecisionSteps; pStep++) {
// Walk forward in px and find the x where px <= p && p < px+1
while (xStep < PrecisionSteps) {
if (px[xStep] > p) break;
xStep++;
}
// Now, px[xStep-1] <= p < px[xStep]
if (xStep == 0) {
xp[pStep] = 0;
} else {
// Find x such that proportionally, x is correct
float fraction = (p - px[xStep - 1]) / (px[xStep] - px[xStep - 1]);
x = (xStep - 1 + fraction) * step;
xp[pStep] = x;
}
p += step;
}
}
/**
* Reverses and scales out x.
*/
static float reverse(float x) {
return (-x * XScale) + 1;
}
/**
* The log function describing the curve.
*/
static float logFunc(float x) {
return 1f - (float) (Math.pow(LogBase, reverse(x))) / (LogBase);
}
/**
* The inverse of the log function describing the curve.
*/
float invLogFunc(float y) {
return (float) (Math.log((1f - reverse(y)) * (LogBase - 1) + 1) / Math.log(LogBase));
}
/**
* Converts from the progress along the curve to a screen coordinate.
*/
int curveProgressToScreenY(float p) {
if (p < 0 || p > 1) return mStackVisibleRect.top + (int) (p * mStackVisibleRect.height());
float pIndex = p * PrecisionSteps;
int pFloorIndex = (int) Math.floor(pIndex);
int pCeilIndex = (int) Math.ceil(pIndex);
float xFraction = 0;
if (pFloorIndex < PrecisionSteps && (pCeilIndex != pFloorIndex)) {
float pFraction = (pIndex - pFloorIndex) / (pCeilIndex - pFloorIndex);
xFraction = (xp[pCeilIndex] - xp[pFloorIndex]) * pFraction;
}
float x = xp[pFloorIndex] + xFraction;
return mStackVisibleRect.top + (int) (x * mStackVisibleRect.height());
}
/**
* Converts from the progress along the curve to a scale.
*/
float curveProgressToScale(float p) {
if (p < 0) return StackPeekMinScale;
if (p > 1) return 1f;
float scaleRange = (1f - StackPeekMinScale);
float scale = StackPeekMinScale + (p * scaleRange);
return scale;
}
/**
* Converts from a screen coordinate to the progress along the curve.
*/
float screenYToCurveProgress(int screenY) {
float x = (float) (screenY - mStackVisibleRect.top) / mStackVisibleRect.height();
if (x < 0 || x > 1) return x;
float xIndex = x * PrecisionSteps;
int xFloorIndex = (int) Math.floor(xIndex);
int xCeilIndex = (int) Math.ceil(xIndex);
float pFraction = 0;
if (xFloorIndex < PrecisionSteps && (xCeilIndex != xFloorIndex)) {
float xFraction = (xIndex - xFloorIndex) / (xCeilIndex - xFloorIndex);
pFraction = (px[xCeilIndex] - px[xFloorIndex]) * xFraction;
}
return px[xFloorIndex] + pFraction;
}
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/views/DeckViewScroller.java
================================================
package com.appeaser.deckview.views;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.widget.OverScroller;
import com.appeaser.deckview.helpers.DeckViewConfig;
import com.appeaser.deckview.utilities.DVUtils;
/**
* Created by Vikram on 02/04/2015.
*/
/* The scrolling logic for a TaskStackView */
public class DeckViewScroller {
public interface DeckViewScrollerCallbacks {
public void onScrollChanged(float p);
}
DeckViewConfig mConfig;
DeckViewLayoutAlgorithm mLayoutAlgorithm;
DeckViewScrollerCallbacks mCb;
float mStackScrollP;
OverScroller mScroller;
ObjectAnimator mScrollAnimator;
float mFinalAnimatedScroll;
public DeckViewScroller(Context context, DeckViewConfig config,
DeckViewLayoutAlgorithm layoutAlgorithm) {
mConfig = config;
mScroller = new OverScroller(context);
mLayoutAlgorithm = layoutAlgorithm;
setStackScroll(getStackScroll());
}
/**
* Resets the task scroller.
*/
public void reset() {
mStackScrollP = 0f;
}
/**
* Sets the callbacks
*/
public void setCallbacks(DeckViewScrollerCallbacks cb) {
mCb = cb;
}
/**
* Gets the current stack scroll
*/
public float getStackScroll() {
return mStackScrollP;
}
/**
* Sets the current stack scroll
*/
public void setStackScroll(float s) {
mStackScrollP = s;
if (mCb != null) {
mCb.onScrollChanged(mStackScrollP);
}
}
/**
* Sets the current stack scroll without calling the callback.
*/
void setStackScrollRaw(float s) {
mStackScrollP = s;
}
/**
* Sets the current stack scroll to the initial state when you first enter recents.
*
* @return whether the stack progress changed.
*/
public boolean setStackScrollToInitialState() {
float prevStackScrollP = mStackScrollP;
setStackScroll(getBoundedStackScroll(mLayoutAlgorithm.mInitialScrollP));
return Float.compare(prevStackScrollP, mStackScrollP) != 0;
}
/**
* Bounds the current scroll if necessary
*/
public boolean boundScroll() {
float curScroll = getStackScroll();
float newScroll = getBoundedStackScroll(curScroll);
if (Float.compare(newScroll, curScroll) != 0) {
setStackScroll(newScroll);
return true;
}
return false;
}
/**
* Bounds the current scroll if necessary, but does not synchronize the stack view with the model.
*/
public boolean boundScrollRaw() {
float curScroll = getStackScroll();
float newScroll = getBoundedStackScroll(curScroll);
if (Float.compare(newScroll, curScroll) != 0) {
setStackScrollRaw(newScroll);
return true;
}
return false;
}
/**
* Returns the bounded stack scroll
*/
float getBoundedStackScroll(float scroll) {
return Math.max(mLayoutAlgorithm.mMinScrollP, Math.min(mLayoutAlgorithm.mMaxScrollP, scroll));
}
/**
* Returns the amount that the aboslute value of how much the scroll is out of bounds.
*/
float getScrollAmountOutOfBounds(float scroll) {
if (scroll < mLayoutAlgorithm.mMinScrollP) {
return Math.abs(scroll - mLayoutAlgorithm.mMinScrollP);
} else if (scroll > mLayoutAlgorithm.mMaxScrollP) {
return Math.abs(scroll - mLayoutAlgorithm.mMaxScrollP);
}
return 0f;
}
/**
* Returns whether the specified scroll is out of bounds
*/
boolean isScrollOutOfBounds() {
return Float.compare(getScrollAmountOutOfBounds(mStackScrollP), 0f) != 0;
}
/**
* Animates the stack scroll into bounds
*/
ObjectAnimator animateBoundScroll() {
float curScroll = getStackScroll();
float newScroll = getBoundedStackScroll(curScroll);
if (Float.compare(newScroll, curScroll) != 0) {
// Start a new scroll animation
animateScroll(curScroll, newScroll, null);
}
return mScrollAnimator;
}
/**
* Animates the stack scroll
*/
void animateScroll(float curScroll, float newScroll, final Runnable postRunnable) {
// Finish any current scrolling animations
if (mScrollAnimator != null && mScrollAnimator.isRunning()) {
setStackScroll(mFinalAnimatedScroll);
mScroller.startScroll(0, progressToScrollRange(mFinalAnimatedScroll), 0, 0, 0);
}
stopScroller();
stopBoundScrollAnimation();
mFinalAnimatedScroll = newScroll;
mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll);
mScrollAnimator.setDuration(mConfig.taskStackScrollDuration);
mScrollAnimator.setInterpolator(mConfig.linearOutSlowInInterpolator);
mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setStackScroll((Float) animation.getAnimatedValue());
}
});
mScrollAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (postRunnable != null) {
postRunnable.run();
}
mScrollAnimator.removeAllListeners();
}
});
mScrollAnimator.start();
}
/**
* Aborts any current stack scrolls
*/
void stopBoundScrollAnimation() {
DVUtils.cancelAnimationWithoutCallbacks(mScrollAnimator);
}
/**
* * OverScroller ***
*/
int progressToScrollRange(float p) {
return (int) (p * mLayoutAlgorithm.mStackVisibleRect.height());
}
float scrollRangeToProgress(int s) {
return (float) s / mLayoutAlgorithm.mStackVisibleRect.height();
}
/**
* Called from the view draw, computes the next scroll.
*/
boolean computeScroll() {
if (mScroller.computeScrollOffset()) {
float scroll = scrollRangeToProgress(mScroller.getCurrY());
setStackScrollRaw(scroll);
if (mCb != null) {
mCb.onScrollChanged(scroll);
}
return true;
}
return false;
}
/**
* Returns whether the overscroller is scrolling.
*/
boolean isScrolling() {
return !mScroller.isFinished();
}
/**
* Stops the scroller and any current fling.
*/
void stopScroller() {
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
}
}
================================================
FILE: deckview/src/main/java/com/appeaser/deckview/views/DeckViewTouchHandler.java
================================================
package com.appeaser.deckview.views;
import android.content.Context;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;
import com.appeaser.deckview.helpers.DeckViewConfig;
import com.appeaser.deckview.helpers.DeckViewSwipeHelper;
import com.appeaser.deckview.utilities.DVConstants;
/**
* Created by Vikram on 02/04/2015.
*/
/* Handles touch events for a TaskStackView. */
public class DeckViewTouchHandler implements DeckViewSwipeHelper.Callback {
static int INACTIVE_POINTER_ID = -1;
DeckViewConfig mConfig;
DeckView mDeckView;
DeckViewScroller mScroller;
VelocityTracker mVelocityTracker;
boolean mIsScrolling;
float mInitialP;
float mLastP;
float mTotalPMotion;
int mInitialMotionX, mInitialMotionY;
int mLastMotionX, mLastMotionY;
int mActivePointerId = INACTIVE_POINTER_ID;
DeckChildView mActiveDeckChildView = null;
int mMinimumVelocity;
int mMaximumVelocity;
// The scroll touch slop is used to calculate when we start scrolling
int mScrollTouchSlop;
// The page touch slop is used to calculate when we start swiping
float mPagingTouchSlop;
DeckViewSwipeHelper mSwipeHelper;
boolean mInterceptedBySwipeHelper;
public DeckViewTouchHandler(Context context, DeckView dv,
DeckViewConfig config, DeckViewScroller scroller) {
ViewConfiguration configuration = ViewConfiguration.get(context);
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mScrollTouchSlop = configuration.getScaledTouchSlop();
mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
mDeckView = dv;
mScroller = scroller;
mConfig = config;
float densityScale = context.getResources().getDisplayMetrics().density;
mSwipeHelper = new DeckViewSwipeHelper(DeckViewSwipeHelper.X, this,
densityScale, mPagingTouchSlop);
mSwipeHelper.setMinAlpha(1f);
}
/**
* Velocity tracker helpers
*/
void initOrResetVelocityTracker() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
} else {
mVelocityTracker.clear();
}
}
void initVelocityTrackerIfNotExists() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
}
void recycleVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
/**
* Returns the view at the specified coordinates
*/
DeckChildView findViewAtPoint(int x, int y) {
int childCount = mDeckView.getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
DeckChildView tv = (DeckChildView) mDeckView.getChildAt(i);
if (tv.getVisibility() == View.VISIBLE) {
if (mDeckView.isTransformedTouchPointInView(x, y, tv)) {
return tv;
}
}
}
return null;
}
/**
* Constructs a simulated motion event for the current stack scroll.
*/
MotionEvent createMotionEventForStackScroll(MotionEvent ev) {
MotionEvent pev = MotionEvent.obtainNoHistory(ev);
pev.setLocation(0, mScroller.progressToScrollRange(mScroller.getStackScroll()));
return pev;
}
/**
* Touch preprocessing for handling below
*/
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Return early if we have no children
boolean hasChildren = (mDeckView.getChildCount() > 0);
if (!hasChildren) {
return false;
}
// Pass through to swipe helper if we are swiping
mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
if (mInterceptedBySwipeHelper) {
return true;
}
boolean wasScrolling = mScroller.isScrolling() ||
(mScroller.mScrollAnimator != null && mScroller.mScrollAnimator.isRunning());
int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
// Save the touch down info
mInitialMotionX = mLastMotionX = (int) ev.getX();
mInitialMotionY = mLastMotionY = (int) ev.getY();
mInitialP = mLastP = mDeckView.getStackAlgorithm().screenYToCurveProgress(mLastMotionY);
mActivePointerId = ev.getPointerId(0);
mActiveDeckChildView = findViewAtPoint(mLastMotionX, mLastMotionY);
// Stop the current scroll if it is still flinging
mScroller.stopScroller();
mScroller.stopBoundScrollAnimation();
// Initialize the velocity tracker
initOrResetVelocityTracker();
mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
break;
}
case MotionEvent.ACTION_MOVE: {
if (mActivePointerId == INACTIVE_POINTER_ID) break;
// Initialize the velocity tracker if necessary
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
int activePointerIndex = ev.findPointerIndex(mActivePointerId);
int y = (int) ev.getY(activePointerIndex);
int x = (int) ev.getX(activePointerIndex);
if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
// Save the touch move info
mIsScrolling = true;
// Disallow parents from intercepting touch events
final ViewParent parent = mDeckView.getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
mLastMotionX = x;
mLastMotionY = y;
mLastP = mDeckView.getStackAlgorithm().screenYToCurveProgress(mLastMotionY);
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
// Animate the scroll back if we've cancelled
mScroller.animateBoundScroll();
// Reset the drag state and the velocity tracker
mIsScrolling = false;
mActivePointerId = INACTIVE_POINTER_ID;
mActiveDeckChildView = null;
mTotalPMotion = 0;
recycleVelocityTracker();
break;
}
}
return wasScrolling || mIsScrolling;
}
/**
* Handles touch events once we have intercepted them
*/
public boolean onTouchEvent(MotionEvent ev) {
// Short circuit if we have no children
boolean hasChildren = (mDeckView.getChildCount() > 0);
if (!hasChildren) {
return false;
}
// Pass through to swipe helper if we are swiping
if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
return true;
}
// Update the velocity tracker
initVelocityTrackerIfNotExists();
int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
// Save the touch down info
mInitialMotionX = mLastMotionX = (int) ev.getX();
mInitialMotionY = mLastMotionY = (int) ev.getY();
mInitialP = mLastP = mDeckView.getStackAlgorithm().screenYToCurveProgress(mLastMotionY);
mActivePointerId = ev.getPointerId(0);
mActiveDeckChildView = findViewAtPoint(mLastMotionX, mLastMotionY);
// Stop the current scroll if it is still flinging
mScroller.stopScroller();
mScroller.stopBoundScrollAnimation();
// Initialize the velocity tracker
initOrResetVelocityTracker();
mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
// Disallow parents from intercepting touch events
final ViewParent parent = mDeckView.getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
break;
}
case MotionEvent.ACTION_POINTER_DOWN: {
final int index = ev.getActionIndex();
mActivePointerId = ev.getPointerId(index);
gitextract_kqwh3f90/ ├── .gitignore ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── appeaser/ │ │ └── deckviewlibrary/ │ │ └── ApplicationTest.java │ └── main/ │ ├── AndroidManifest.xml │ └── res/ │ └── values/ │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── deckview/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── appeaser/ │ │ └── deckview/ │ │ └── ApplicationTest.java │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── appeaser/ │ │ └── deckview/ │ │ ├── helpers/ │ │ │ ├── DeckChildViewTransform.java │ │ │ ├── DeckViewConfig.java │ │ │ ├── DeckViewSwipeHelper.java │ │ │ └── FakeShadowDrawable.java │ │ ├── utilities/ │ │ │ ├── DVConstants.java │ │ │ ├── DVUtils.java │ │ │ ├── DozeTrigger.java │ │ │ └── ReferenceCountedTrigger.java │ │ └── views/ │ │ ├── AnimateableDeckChildViewBounds.java │ │ ├── DeckChildView.java │ │ ├── DeckChildViewHeader.java │ │ ├── DeckChildViewThumbnail.java │ │ ├── DeckView.java │ │ ├── DeckViewLayoutAlgorithm.java │ │ ├── DeckViewScroller.java │ │ ├── DeckViewTouchHandler.java │ │ ├── FixedSizeImageView.java │ │ ├── ViewAnimation.java │ │ └── ViewPool.java │ └── res/ │ ├── drawable-v21/ │ │ ├── deck_child_view_button_bg.xml │ │ ├── deck_child_view_dismiss_dark.xml │ │ ├── deck_child_view_dismiss_light.xml │ │ ├── deck_child_view_header_bg.xml │ │ └── deck_child_view_header_bg_color.xml │ ├── interpolator-v21/ │ │ ├── decelerate_quint.xml │ │ ├── fast_out_linear_in.xml │ │ ├── fast_out_slow_in.xml │ │ └── linear_out_slow_in.xml │ ├── layout/ │ │ ├── deck_child_view.xml │ │ └── deck_child_view_header.xml │ ├── values/ │ │ ├── colors.xml │ │ ├── config.xml │ │ ├── dimens.xml │ │ └── strings.xml │ ├── values-land/ │ │ └── dimens.xml │ ├── values-sw600dp/ │ │ └── dimens.xml │ ├── values-sw600dp-land/ │ │ └── dimens.xml │ └── values-sw720dp/ │ └── dimens.xml ├── deckviewsample/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── appeaser/ │ │ └── deckviewsample/ │ │ └── ApplicationTest.java │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── appeaser/ │ │ └── deckviewsample/ │ │ ├── Datum.java │ │ └── DeckViewSampleActivity.java │ └── res/ │ ├── drawable-v21/ │ │ └── box.xml │ ├── layout/ │ │ └── activity_deck_view_sample.xml │ ├── menu/ │ │ └── menu_deck_view_sample.xml │ ├── values/ │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── values-v21/ │ │ └── styles.xml │ └── values-w820dp/ │ └── dimens.xml ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle
SYMBOL INDEX (411 symbols across 24 files)
FILE: app/src/androidTest/java/com/appeaser/deckviewlibrary/ApplicationTest.java
class ApplicationTest (line 9) | public class ApplicationTest extends ApplicationTestCase<Application> {
method ApplicationTest (line 10) | public ApplicationTest() {
FILE: deckview/src/androidTest/java/com/appeaser/deckview/ApplicationTest.java
class ApplicationTest (line 9) | public class ApplicationTest extends ApplicationTestCase<Application> {
method ApplicationTest (line 10) | public ApplicationTest() {
FILE: deckview/src/main/java/com/appeaser/deckview/helpers/DeckChildViewTransform.java
class DeckChildViewTransform (line 13) | public class DeckChildViewTransform {
method DeckChildViewTransform (line 23) | public DeckChildViewTransform() {
method DeckChildViewTransform (line 27) | public DeckChildViewTransform(DeckChildViewTransform o) {
method reset (line 41) | public void reset() {
method hasAlphaChangedFrom (line 55) | public boolean hasAlphaChangedFrom(float v) {
method hasScaleChangedFrom (line 59) | public boolean hasScaleChangedFrom(float v) {
method hasTranslationYChangedFrom (line 63) | public boolean hasTranslationYChangedFrom(float v) {
method hasTranslationZChangedFrom (line 67) | public boolean hasTranslationZChangedFrom(float v) {
method applyToTaskView (line 74) | public void applyToTaskView(View v, int duration, Interpolator interp,...
method reset (line 131) | public static void reset(View v) {
method toString (line 140) | @Override
FILE: deckview/src/main/java/com/appeaser/deckview/helpers/DeckViewConfig.java
class DeckViewConfig (line 23) | public class DeckViewConfig {
method DeckViewConfig (line 153) | private DeckViewConfig(Context context) {
method reinitialize (line 171) | public static DeckViewConfig reinitialize(Context context) {
method getInstance (line 188) | public static DeckViewConfig getInstance() {
method update (line 195) | void update(Context context) {
method updateSystemInsets (line 295) | public void updateSystemInsets(Rect insets) {
method updateSearchBarAppWidgetId (line 302) | public void updateSearchBarAppWidgetId(Context context, int appWidgetI...
method updateOnReinitialize (line 309) | void updateOnReinitialize(Context context/*, SystemServicesProxy ssp*/) {
method updateOnConfigurationChange (line 317) | public void updateOnConfigurationChange() {
method getTaskStackBounds (line 328) | public void getTaskStackBounds(int windowWidth, int windowHeight, int ...
FILE: deckview/src/main/java/com/appeaser/deckview/helpers/DeckViewSwipeHelper.java
class DeckViewSwipeHelper (line 24) | public class DeckViewSwipeHelper {
method DeckViewSwipeHelper (line 63) | public DeckViewSwipeHelper(int swipeDirection, Callback callback, floa...
method setDensityScale (line 72) | public void setDensityScale(float densityScale) {
method setPagingTouchSlop (line 76) | public void setPagingTouchSlop(float pagingTouchSlop) {
method cancelOngoingDrag (line 80) | public void cancelOngoingDrag() {
method resetTranslation (line 92) | public void resetTranslation(View v) {
method getPos (line 96) | private float getPos(MotionEvent ev) {
method getTranslation (line 100) | private float getTranslation(View v) {
method getVelocity (line 104) | private float getVelocity(VelocityTracker vt) {
method createTranslationAnimation (line 109) | private ObjectAnimator createTranslationAnimation(View v, float newPos) {
method getPerpendicularVelocity (line 115) | private float getPerpendicularVelocity(VelocityTracker vt) {
method setTranslation (line 120) | private void setTranslation(View v, float translate) {
method getSize (line 128) | private float getSize(View v) {
method setMinAlpha (line 133) | public void setMinAlpha(float minAlpha) {
method getAlphaForOffset (line 137) | float getAlphaForOffset(View view) {
method isLayoutRtl (line 155) | @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
method onInterceptTouchEvent (line 164) | public boolean onInterceptTouchEvent(MotionEvent ev) {
method dismissChild (line 206) | private void dismissChild(final View view, float velocity) {
method snapChild (line 249) | private void snapChild(final View view, float velocity) {
method onTouchEvent (line 276) | public boolean onTouchEvent(MotionEvent ev) {
method setSwipeAmount (line 304) | private void setSwipeAmount(float amount) {
method isValidSwipeDirection (line 324) | private boolean isValidSwipeDirection(float amount) {
method endSwipe (line 337) | private void endSwipe(VelocityTracker velocityTracker) {
type Callback (line 364) | public interface Callback {
method getChildAtPosition (line 365) | View getChildAtPosition(MotionEvent ev);
method canChildBeDismissed (line 367) | boolean canChildBeDismissed(View v);
method onBeginDrag (line 369) | void onBeginDrag(View v);
method onSwipeChanged (line 371) | void onSwipeChanged(View v, float delta);
method onChildDismissed (line 373) | void onChildDismissed(View v);
method onSnapBackCompleted (line 375) | void onSnapBackCompleted(View v);
method onDragCancelled (line 377) | void onDragCancelled(View v);
FILE: deckview/src/main/java/com/appeaser/deckview/helpers/FakeShadowDrawable.java
class FakeShadowDrawable (line 29) | public class FakeShadowDrawable extends Drawable {
method FakeShadowDrawable (line 72) | public FakeShadowDrawable(Resources resources, DeckViewConfig config) {
method setAlpha (line 86) | @Override
method onBoundsChange (line 92) | @Override
method setShadowSize (line 98) | void setShadowSize(float shadowSize, float maxShadowSize) {
method getPadding (line 121) | @Override
method calculateVerticalPadding (line 131) | static float calculateVerticalPadding(float maxShadowSize, float corne...
method calculateHorizontalPadding (line 140) | static float calculateHorizontalPadding(float maxShadowSize, float cor...
method setColorFilter (line 149) | @Override
method getOpacity (line 155) | @Override
method draw (line 160) | @Override
method drawShadow (line 171) | private void drawShadow(Canvas canvas) {
method buildShadowCorners (line 219) | private void buildShadowCorners() {
method buildComponents (line 253) | private void buildComponents(Rect bounds) {
method getMinWidth (line 263) | float getMinWidth() {
method getMinHeight (line 269) | float getMinHeight() {
FILE: deckview/src/main/java/com/appeaser/deckview/utilities/DVConstants.java
class DVConstants (line 6) | public class DVConstants {
class DebugFlags (line 7) | public static class DebugFlags {
class App (line 11) | public static class App {
class Values (line 43) | public static class Values {
class App (line 44) | public static class App {
class DView (line 51) | public static class DView {
FILE: deckview/src/main/java/com/appeaser/deckview/utilities/DVUtils.java
class DVUtils (line 19) | public class DVUtils {
method calculateTranslationAnimationDuration (line 39) | public static int calculateTranslationAnimationDuration(int distancePx) {
method calculateTranslationAnimationDuration (line 43) | public static int calculateTranslationAnimationDuration(int distancePx...
method scaleRectAboutCenter (line 52) | public static void scaleRectAboutCenter(Rect r, float scale) {
method mapCoordInDescendentToSelf (line 68) | public static float mapCoordInDescendentToSelf(View descendant, View r...
method mapCoordInSelfToDescendent (line 106) | public static float mapCoordInSelfToDescendent(View descendant, View r...
method computeContrastBetweenColors (line 146) | public static float computeContrastBetweenColors(int bg, int fg) {
method getColorWithOverlay (line 169) | public static int getColorWithOverlay(int baseColor, int overlayColor,...
method setShadowProperty (line 182) | public static void setShadowProperty(String property, String value)
method cancelAnimationWithoutCallbacks (line 191) | public static void cancelAnimationWithoutCallbacks(Animator animator) {
method oops (line 199) | void oops() {
method set (line 203) | @Override
method reset (line 208) | @Override
method setTranslate (line 213) | @Override
method setScale (line 218) | @Override
method setScale (line 223) | @Override
method setRotate (line 228) | @Override
method setRotate (line 233) | @Override
method setSinCos (line 238) | @Override
method setSinCos (line 243) | @Override
method setSkew (line 248) | @Override
method setSkew (line 253) | @Override
method setConcat (line 258) | @Override
method preTranslate (line 264) | @Override
method preScale (line 270) | @Override
method preScale (line 276) | @Override
method preRotate (line 282) | @Override
method preRotate (line 288) | @Override
method preSkew (line 294) | @Override
method preSkew (line 300) | @Override
method preConcat (line 306) | @Override
method postTranslate (line 312) | @Override
method postScale (line 318) | @Override
method postScale (line 324) | @Override
method postRotate (line 330) | @Override
method postRotate (line 336) | @Override
method postSkew (line 342) | @Override
method postSkew (line 348) | @Override
method postConcat (line 354) | @Override
method setRectToRect (line 360) | @Override
method setPolyToPoly (line 366) | @Override
method setValues (line 373) | @Override
FILE: deckview/src/main/java/com/appeaser/deckview/utilities/DozeTrigger.java
class DozeTrigger (line 13) | public class DozeTrigger {
method run (line 24) | @Override
method DozeTrigger (line 32) | public DozeTrigger(int dozeDurationSeconds, Runnable sleepRunnable) {
method startDozing (line 41) | public void startDozing() {
method stopDozing (line 49) | public void stopDozing() {
method poke (line 57) | public void poke() {
method forcePoke (line 66) | void forcePoke() {
method isDozing (line 75) | public boolean isDozing() {
method hasTriggered (line 82) | public boolean hasTriggered() {
method resetTrigger (line 89) | public void resetTrigger() {
FILE: deckview/src/main/java/com/appeaser/deckview/utilities/ReferenceCountedTrigger.java
class ReferenceCountedTrigger (line 17) | public class ReferenceCountedTrigger {
method run (line 27) | @Override
method run (line 33) | @Override
method ReferenceCountedTrigger (line 39) | public ReferenceCountedTrigger(Context context, Runnable firstIncRunna...
method increment (line 50) | public void increment() {
method incrementAsRunnable (line 63) | public Runnable incrementAsRunnable() {
method addLastDecrementRunnable (line 70) | public void addLastDecrementRunnable(Runnable r) {
method decrement (line 82) | public void decrement() {
method decrementAsRunnable (line 102) | public Runnable decrementAsRunnable() {
method decrementOnAnimationEnd (line 109) | public Animator.AnimatorListener decrementOnAnimationEnd() {
method getCount (line 121) | public int getCount() {
FILE: deckview/src/main/java/com/appeaser/deckview/views/AnimateableDeckChildViewBounds.java
class AnimateableDeckChildViewBounds (line 14) | public class AnimateableDeckChildViewBounds extends ViewOutlineProvider {
method AnimateableDeckChildViewBounds (line 25) | public AnimateableDeckChildViewBounds(DeckChildView source, int corner...
method getOutline (line 32) | @Override
method setAlpha (line 44) | void setAlpha(float alpha) {
method setClipBottom (line 54) | public void setClipBottom(int bottom) {
method getClipBottom (line 69) | public int getClipBottom() {
method updateClipBounds (line 73) | private void updateClipBounds() {
FILE: deckview/src/main/java/com/appeaser/deckview/views/DeckChildView.java
class DeckChildView (line 33) | public class DeckChildView<T> extends FrameLayout implements
type DeckChildViewCallbacks (line 39) | interface DeckChildViewCallbacks<T> {
method onDeckChildViewAppIconClicked (line 40) | public void onDeckChildViewAppIconClicked(DeckChildView dcv);
method onDeckChildViewAppInfoClicked (line 42) | public void onDeckChildViewAppInfoClicked(DeckChildView dcv);
method onDeckChildViewClicked (line 44) | public void onDeckChildViewClicked(DeckChildView<T> dcv, T key);
method onDeckChildViewDismissed (line 46) | public void onDeckChildViewDismissed(DeckChildView<T> dcv);
method onDeckChildViewClipStateChanged (line 48) | public void onDeckChildViewClipStateChanged(DeckChildView dcv);
method onDeckChildViewFocusChanged (line 50) | public void onDeckChildViewFocusChanged(DeckChildView<T> dcv, boolea...
method onAnimationUpdate (line 80) | @Override
method DeckChildView (line 87) | public DeckChildView(Context context) {
method DeckChildView (line 91) | public DeckChildView(Context context, AttributeSet attrs) {
method DeckChildView (line 95) | public DeckChildView(Context context, AttributeSet attrs, int defStyle...
method DeckChildView (line 99) | public DeckChildView(Context context, AttributeSet attrs, int defStyle...
method setCallbacks (line 116) | void setCallbacks(DeckChildViewCallbacks cb) {
method reset (line 123) | void reset() {
method getAttachedKey (line 133) | T getAttachedKey() {
method getViewBounds (line 140) | AnimateableDeckChildViewBounds getViewBounds() {
method onFinishInflate (line 144) | @Override
method onMeasure (line 153) | @Override
method updateViewPropertiesToTaskTransform (line 180) | void updateViewPropertiesToTaskTransform(DeckChildViewTransform toTran...
method updateViewPropertiesToTaskTransform (line 184) | void updateViewPropertiesToTaskTransform(DeckChildViewTransform toTran...
method resetViewProperties (line 205) | void resetViewProperties() {
method prepareTaskTransformForFilterTaskHidden (line 215) | void prepareTaskTransformForFilterTaskHidden(DeckChildViewTransform to...
method prepareTaskTransformForFilterTaskVisible (line 226) | void prepareTaskTransformForFilterTaskVisible(DeckChildViewTransform f...
method prepareEnterRecentsAnimation (line 235) | void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask,
method startEnterRecentsAnimation (line 265) | void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterConte...
method startExitToHomeAnimation (line 314) | void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
method startLaunchTaskAnimation (line 329) | void startLaunchTaskAnimation(final Runnable postAnimRunnable, boolean...
method startDeleteTaskAnimation (line 362) | void startDeleteTaskAnimation(final Runnable r) {
method startNoUserInteractionAnimation (line 392) | void startNoUserInteractionAnimation() {
method setNoUserInteractionState (line 399) | void setNoUserInteractionState() {
method resetNoUserInteractionState (line 406) | void resetNoUserInteractionState() {
method dismissTask (line 413) | void dismissTask() {
method shouldClipViewInStack (line 430) | boolean shouldClipViewInStack() {
method setClipViewInStack (line 437) | public void setClipViewInStack(boolean clip) {
method setTaskProgress (line 449) | public void setTaskProgress(float p) {
method getTaskProgress (line 458) | public float getTaskProgress() {
method setDim (line 465) | public void setDim(int dim) {
method getDim (line 490) | public int getDim() {
method animateDimToProgress (line 497) | void animateDimToProgress(int delay, int duration, Animator.AnimatorLi...
method getDimFromTaskProgress (line 514) | int getDimFromTaskProgress() {
method updateDimFromTaskProgress (line 522) | void updateDimFromTaskProgress() {
method setFocusedTask (line 533) | public void setFocusedTask(boolean animateFocusedState) {
method unsetFocusedTask (line 557) | void unsetFocusedTask() {
method onFocusChanged (line 576) | @Override
method isFocusedTask (line 587) | public boolean isFocusedTask() {
method enableFocusAnimations (line 594) | void enableFocusAnimations() {
method onTaskBound (line 608) | public void onTaskBound(T key) {
method isBound (line 612) | private boolean isBound() {
method onTaskUnbound (line 619) | public void onTaskUnbound() {
method getThumbnail (line 623) | public Bitmap getThumbnail() {
method onDataLoaded (line 631) | public void onDataLoaded(T key, Bitmap thumbnail, Drawable headerIcon,
method onDataUnloaded (line 650) | public void onDataUnloaded() {
method setTouchEnabled (line 668) | public void setTouchEnabled(boolean enabled) {
method onClick (line 676) | @Override
method onLongClick (line 706) | @Override
FILE: deckview/src/main/java/com/appeaser/deckview/views/DeckChildViewHeader.java
class DeckChildViewHeader (line 40) | public class DeckChildViewHeader extends FrameLayout {
method DeckChildViewHeader (line 67) | public DeckChildViewHeader(Context context) {
method DeckChildViewHeader (line 71) | public DeckChildViewHeader(Context context, AttributeSet attrs) {
method DeckChildViewHeader (line 75) | public DeckChildViewHeader(Context context, AttributeSet attrs, int de...
method DeckChildViewHeader (line 79) | public DeckChildViewHeader(Context context, AttributeSet attrs, int de...
method onTouchEvent (line 109) | @Override
method onFinishInflate (line 117) | @Override
method onDraw (line 142) | @Override
method hasOverlappingRendering (line 154) | @Override
method setDimAlpha (line 163) | void setDimAlpha(int alpha) {
method getSecondaryColor (line 173) | int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) {
method rebindToTask (line 182) | public void rebindToTask(Drawable headerIcon, String headerTitle, int ...
method unbindFromTask (line 208) | void unbindFromTask() {
method startLaunchTaskDismissAnimation (line 215) | void startLaunchTaskDismissAnimation() {
method startNoUserInteractionAnimation (line 231) | void startNoUserInteractionAnimation() {
method setNoUserInteractionState (line 248) | void setNoUserInteractionState() {
method resetNoUserInteractionState (line 259) | void resetNoUserInteractionState() {
method onCreateDrawableState (line 263) | @Override
method onTaskViewFocusChanged (line 274) | void onTaskViewFocusChanged(boolean focused, boolean animateFocusedSta...
FILE: deckview/src/main/java/com/appeaser/deckview/views/DeckChildViewThumbnail.java
class DeckChildViewThumbnail (line 31) | public class DeckChildViewThumbnail extends View {
method onAnimationUpdate (line 49) | @Override
method DeckChildViewThumbnail (line 65) | public DeckChildViewThumbnail(Context context) {
method DeckChildViewThumbnail (line 69) | public DeckChildViewThumbnail(Context context, AttributeSet attrs) {
method DeckChildViewThumbnail (line 73) | public DeckChildViewThumbnail(Context context, AttributeSet attrs, int...
method DeckChildViewThumbnail (line 77) | public DeckChildViewThumbnail(Context context, AttributeSet attrs, int...
method onFinishInflate (line 85) | @Override
method onLayout (line 91) | @Override
method onDraw (line 100) | @Override
method setThumbnail (line 114) | void setThumbnail(Bitmap bm) {
method updateThumbnailPaintFilter (line 133) | void updateThumbnailPaintFilter() {
method updateThumbnailScale (line 156) | void updateThumbnailScale() {
method updateClipToTaskBar (line 166) | void updateClipToTaskBar(View taskBar) {
method updateThumbnailVisibility (line 177) | void updateThumbnailVisibility(int clipBottom) {
method setDimAlpha (line 192) | public void setDimAlpha(float dimAlpha) {
method rebindToTask (line 201) | void rebindToTask(Bitmap thumbnail) {
method unbindFromTask (line 212) | void unbindFromTask() {
method getThumbnail (line 218) | public Bitmap getThumbnail() {
method onFocusChanged (line 225) | void onFocusChanged(boolean focused) {
method prepareEnterRecentsAnimation (line 241) | void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask) {
method startEnterRecentsAnimation (line 253) | void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) {
method startLaunchTaskAnimation (line 261) | void startLaunchTaskAnimation(Runnable postAnimRunnable) {
method startFadeAnimation (line 268) | void startFadeAnimation(float finalAlpha, int delay, int duration, fin...
FILE: deckview/src/main/java/com/appeaser/deckview/views/DeckView.java
class DeckView (line 32) | public class DeckView<T> extends FrameLayout implements /*TaskStack.Task...
method onAnimationUpdate (line 67) | @Override
method DeckView (line 73) | public DeckView(Context context) {
method DeckView (line 77) | public DeckView(Context context, AttributeSet attrs) {
method DeckView (line 81) | public DeckView(Context context, AttributeSet attrs, int defStyleAttr) {
method DeckView (line 85) | public DeckView(Context context, AttributeSet attrs, int defStyleAttr,...
method initialize (line 91) | public void initialize(Callback<T> callback) {
method reset (line 118) | void reset() {
method requestSynchronizeStackViewsWithModel (line 155) | void requestSynchronizeStackViewsWithModel() {
method requestSynchronizeStackViewsWithModel (line 159) | void requestSynchronizeStackViewsWithModel(int duration) {
method requestUpdateStackViewsClip (line 175) | void requestUpdateStackViewsClip() {
method getChildViewForTask (line 185) | public DeckChildView getChildViewForTask(T key) {
method getStackAlgorithm (line 199) | public DeckViewLayoutAlgorithm getStackAlgorithm() {
method updateStackTransforms (line 206) | private boolean updateStackTransforms(ArrayList<DeckChildViewTransform...
method synchronizeStackViewsWithModel (line 266) | boolean synchronizeStackViewsWithModel() {
method clipTaskViews (line 329) | void clipTaskViews() {
method setStackInsetRect (line 376) | public void setStackInsetRect(Rect r) {
method updateMinMaxScroll (line 383) | void updateMinMaxScroll(boolean boundScrollToNewMinMax, boolean launch...
method getScroller (line 397) | public DeckViewScroller getScroller() {
method focusTask (line 404) | void focusTask(int childIndex, boolean scrollToNewPosition, final bool...
method ensureFocusedTask (line 450) | public boolean ensureFocusedTask() {
method focusNextTask (line 480) | public void focusNextTask(boolean forward, boolean animateFocusedState) {
method dismissFocusedTask (line 496) | public void dismissFocusedTask() {
method resetFocusedTask (line 512) | void resetFocusedTask() {
method onInitializeAccessibilityEvent (line 522) | @Override
method onInterceptTouchEvent (line 537) | @Override
method onTouchEvent (line 542) | @Override
method onGenericMotionEvent (line 547) | @Override
method computeScroll (line 552) | @Override
method computeRects (line 565) | public void computeRects(int windowWidth, int windowHeight, Rect taskS...
method getCurrentChildIndex (line 574) | public int getCurrentChildIndex() {
method scrollToChild (line 590) | public void scrollToChild(int childIndex) {
method computeStackVisibilityReport (line 609) | public DeckViewLayoutAlgorithm.VisibilityReport computeStackVisibility...
method onMeasure (line 617) | @Override
method onLayout (line 668) | @Override
method onFirstLayout (line 694) | void onFirstLayout() {
method showDeck (line 731) | void showDeck(Context context) {
method startEnterRecentsAnimation (line 746) | public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterCont...
method onApplyWindowInsets (line 785) | @Override
method hideDeck (line 797) | void hideDeck(Context context, Runnable finishRunnable) {
method startExitToHomeAnimation (line 812) | public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext...
method startLaunchTaskAnimation (line 829) | public void startLaunchTaskAnimation(DeckChildView tv, Runnable r, boo...
method onRecentsHidden (line 846) | void onRecentsHidden() {
method isTransformedTouchPointInView (line 850) | public boolean isTransformedTouchPointInView(float x, float y, View ch...
method onUserInteraction (line 864) | void onUserInteraction() {
method createView (line 873) | @Override
method prepareViewToEnterPool (line 878) | @Override
method prepareViewToLeavePool (line 896) | @Override
method hasPreferredData (line 950) | @Override
method onDeckChildViewAppIconClicked (line 959) | @Override
method onDeckChildViewAppInfoClicked (line 964) | @Override
method onDeckChildViewClicked (line 969) | @Override
method onDeckChildViewDismissed (line 976) | @Override
method onStackTaskRemoved (line 999) | public void onStackTaskRemoved(DeckChildView<T> removedView) {
method notifyDataSetChanged (line 1052) | public void notifyDataSetChanged() {
method onDeckChildViewClipStateChanged (line 1094) | @Override
method onDeckChildViewFocusChanged (line 1101) | @Override
method onScrollChanged (line 1112) | @Override
method notifyDataSetChangedOld (line 1119) | public void notifyDataSetChangedOld() {
type Callback (line 1162) | public interface Callback<T> {
method getData (line 1163) | public ArrayList<T> getData();
method loadViewData (line 1165) | public void loadViewData(WeakReference<DeckChildView<T>> dcv, T item);
method unloadViewData (line 1167) | public void unloadViewData(T item);
method onViewDismissed (line 1169) | public void onViewDismissed(T item);
method onItemClick (line 1171) | public void onItemClick(T item);
method onNoViewsToDeck (line 1173) | public void onNoViewsToDeck();
FILE: deckview/src/main/java/com/appeaser/deckview/views/DeckViewLayoutAlgorithm.java
class DeckViewLayoutAlgorithm (line 21) | public class DeckViewLayoutAlgorithm<T> {
class VisibilityReport (line 27) | public class VisibilityReport {
method VisibilityReport (line 34) | VisibilityReport(int tasks, int thumbnails) {
method DeckViewLayoutAlgorithm (line 63) | public DeckViewLayoutAlgorithm(DeckViewConfig config) {
method computeRects (line 73) | public void computeRects(int windowWidth, int windowHeight, Rect taskS...
method computeMinMaxScroll (line 100) | void computeMinMaxScroll(ArrayList<T> data, boolean launchedWithAltTab,
method computeStackVisibilityReport (line 163) | public VisibilityReport computeStackVisibilityReport(ArrayList<T> data) {
method getStackTransform (line 219) | public DeckChildViewTransform getStackTransform(T key, float stackScroll,
method getStackTransform (line 234) | public DeckChildViewTransform getStackTransform(float taskProgress, fl...
method getUntransformedTaskViewSize (line 273) | public Rect getUntransformedTaskViewSize() {
method getStackScrollForTask (line 282) | public float getStackScrollForTask(T key) {
method initializeCurve (line 290) | public static void initializeCurve() {
method reverse (line 347) | static float reverse(float x) {
method logFunc (line 354) | static float logFunc(float x) {
method invLogFunc (line 361) | float invLogFunc(float y) {
method curveProgressToScreenY (line 368) | int curveProgressToScreenY(float p) {
method curveProgressToScale (line 385) | float curveProgressToScale(float p) {
method screenYToCurveProgress (line 396) | float screenYToCurveProgress(int screenY) {
FILE: deckview/src/main/java/com/appeaser/deckview/views/DeckViewScroller.java
class DeckViewScroller (line 17) | public class DeckViewScroller {
type DeckViewScrollerCallbacks (line 18) | public interface DeckViewScrollerCallbacks {
method onScrollChanged (line 19) | public void onScrollChanged(float p);
method DeckViewScroller (line 32) | public DeckViewScroller(Context context, DeckViewConfig config,
method reset (line 43) | public void reset() {
method setCallbacks (line 50) | public void setCallbacks(DeckViewScrollerCallbacks cb) {
method getStackScroll (line 57) | public float getStackScroll() {
method setStackScroll (line 64) | public void setStackScroll(float s) {
method setStackScrollRaw (line 74) | void setStackScrollRaw(float s) {
method setStackScrollToInitialState (line 83) | public boolean setStackScrollToInitialState() {
method boundScroll (line 92) | public boolean boundScroll() {
method boundScrollRaw (line 105) | public boolean boundScrollRaw() {
method getBoundedStackScroll (line 118) | float getBoundedStackScroll(float scroll) {
method getScrollAmountOutOfBounds (line 125) | float getScrollAmountOutOfBounds(float scroll) {
method isScrollOutOfBounds (line 137) | boolean isScrollOutOfBounds() {
method animateBoundScroll (line 144) | ObjectAnimator animateBoundScroll() {
method animateScroll (line 157) | void animateScroll(float curScroll, float newScroll, final Runnable po...
method stopBoundScrollAnimation (line 191) | void stopBoundScrollAnimation() {
method progressToScrollRange (line 199) | int progressToScrollRange(float p) {
method scrollRangeToProgress (line 203) | float scrollRangeToProgress(int s) {
method computeScroll (line 210) | boolean computeScroll() {
method isScrolling (line 225) | boolean isScrolling() {
method stopScroller (line 232) | void stopScroller() {
FILE: deckview/src/main/java/com/appeaser/deckview/views/DeckViewTouchHandler.java
class DeckViewTouchHandler (line 19) | public class DeckViewTouchHandler implements DeckViewSwipeHelper.Callback {
method DeckViewTouchHandler (line 47) | public DeckViewTouchHandler(Context context, DeckView dv,
method initOrResetVelocityTracker (line 67) | void initOrResetVelocityTracker() {
method initVelocityTrackerIfNotExists (line 75) | void initVelocityTrackerIfNotExists() {
method recycleVelocityTracker (line 81) | void recycleVelocityTracker() {
method findViewAtPoint (line 91) | DeckChildView findViewAtPoint(int x, int y) {
method createMotionEventForStackScroll (line 107) | MotionEvent createMotionEventForStackScroll(MotionEvent ev) {
method onInterceptTouchEvent (line 116) | public boolean onInterceptTouchEvent(MotionEvent ev) {
method onTouchEvent (line 193) | public boolean onTouchEvent(MotionEvent ev) {
method onGenericMotionEvent (line 338) | public boolean onGenericMotionEvent(MotionEvent ev) {
method getChildAtPosition (line 365) | @Override
method canChildBeDismissed (line 370) | @Override
method onBeginDrag (line 375) | @Override
method onSwipeChanged (line 389) | @Override
method onChildDismissed (line 394) | @Override
method onSnapBackCompleted (line 405) | @Override
method onDragCancelled (line 414) | @Override
FILE: deckview/src/main/java/com/appeaser/deckview/views/FixedSizeImageView.java
class FixedSizeImageView (line 17) | public class FixedSizeImageView extends ImageView {
method FixedSizeImageView (line 22) | public FixedSizeImageView(Context context) {
method FixedSizeImageView (line 26) | public FixedSizeImageView(Context context, AttributeSet attrs) {
method FixedSizeImageView (line 30) | public FixedSizeImageView(Context context, AttributeSet attrs, int def...
method FixedSizeImageView (line 34) | public FixedSizeImageView(Context context, AttributeSet attrs, int def...
method requestLayout (line 38) | @Override
method invalidate (line 45) | @Override
method setImageDrawable (line 52) | @Override
FILE: deckview/src/main/java/com/appeaser/deckview/views/ViewAnimation.java
class ViewAnimation (line 13) | public class ViewAnimation {
class TaskViewEnterContext (line 16) | public static class TaskViewEnterContext {
method TaskViewEnterContext (line 36) | public TaskViewEnterContext(ReferenceCountedTrigger t) {
class TaskViewExitContext (line 42) | public static class TaskViewExitContext {
method TaskViewExitContext (line 50) | public TaskViewExitContext(ReferenceCountedTrigger t) {
FILE: deckview/src/main/java/com/appeaser/deckview/views/ViewPool.java
class ViewPool (line 12) | public class ViewPool<V, T> {
type ViewPoolConsumer (line 15) | public interface ViewPoolConsumer<V, T> {
method createView (line 16) | public V createView(Context context);
method prepareViewToEnterPool (line 18) | public void prepareViewToEnterPool(V v);
method prepareViewToLeavePool (line 20) | public void prepareViewToLeavePool(V v, T prepareData, boolean isNew...
method hasPreferredData (line 22) | public boolean hasPreferredData(V v, T preferredData);
method ViewPool (line 32) | public ViewPool(Context context, ViewPoolConsumer<V, T> viewCreator) {
method returnViewToPool (line 40) | void returnViewToPool(V v) {
method pickUpViewFromPool (line 48) | V pickUpViewFromPool(T preferredData, T prepareData) {
method poolViewIterator (line 77) | Iterator<V> poolViewIterator() {
FILE: deckviewsample/src/androidTest/java/com/appeaser/deckviewsample/ApplicationTest.java
class ApplicationTest (line 9) | public class ApplicationTest extends ApplicationTestCase<Application> {
method ApplicationTest (line 10) | public ApplicationTest() {
FILE: deckviewsample/src/main/java/com/appeaser/deckviewsample/Datum.java
class Datum (line 19) | public class Datum implements Parcelable {
method Datum (line 25) | public Datum() {
method describeContents (line 29) | @Override
method Datum (line 34) | public Datum(Parcel in) {
method readFromParcel (line 38) | public void readFromParcel(Parcel in) {
method writeToParcel (line 44) | @Override
method createFromParcel (line 52) | public Datum createFromParcel(Parcel in) {
method newArray (line 56) | public Datum[] newArray(int size) {
method equals (line 61) | @Override
FILE: deckviewsample/src/main/java/com/appeaser/deckviewsample/DeckViewSampleActivity.java
class DeckViewSampleActivity (line 29) | public class DeckViewSampleActivity extends Activity {
method onCreate (line 47) | @Override
method loadViewDataInternal (line 131) | void loadViewDataInternal(final Datum datum,
method onCreateOptionsMenu (line 171) | @Override
method onOptionsItemSelected (line 178) | @Override
method onSaveInstanceState (line 222) | @Override
method generateUniqueKey (line 234) | private static int generateUniqueKey() {
Condensed preview — 74 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (267K chars).
[
{
"path": ".gitignore",
"chars": 2753,
"preview": "#################\n## AndroidStudio\n#################\n\n.gradle\nproject.properties\n.idea\ngen\n*.class\nout\n*.iml\n\n##########"
},
{
"path": "README.md",
"chars": 286,
"preview": "# DeckView\nA ViewGroup that mimics Android (Lollipop) Recent apps screen layout.\n\n######Note: \nDeckView is **not** a tru"
},
{
"path": "app/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "app/build.gradle",
"chars": 540,
"preview": "apply plugin: 'com.android.application'\n\nandroid {\n compileSdkVersion 22\n buildToolsVersion \"22.0.1\"\n\n defaultC"
},
{
"path": "app/proguard-rules.pro",
"chars": 700,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in C:"
},
{
"path": "app/src/androidTest/java/com/appeaser/deckviewlibrary/ApplicationTest.java",
"chars": 359,
"preview": "package com.appeaser.deckviewlibrary;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * "
},
{
"path": "app/src/main/AndroidManifest.xml",
"chars": 324,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.appeaser.deckviewlibrary\">\n\n <a"
},
{
"path": "app/src/main/res/values/strings.xml",
"chars": 78,
"preview": "<resources>\n <string name=\"app_name\">DeckViewLibrary</string>\n</resources>\n"
},
{
"path": "app/src/main/res/values/styles.xml",
"chars": 197,
"preview": "<resources>\n\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"android:Theme.Holo.Light.DarkAction"
},
{
"path": "build.gradle",
"chars": 436,
"preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n r"
},
{
"path": "deckview/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "deckview/build.gradle",
"chars": 483,
"preview": "apply plugin: 'com.android.library'\n\nandroid {\n compileSdkVersion 22\n buildToolsVersion \"22.0.1\"\n\n defaultConfi"
},
{
"path": "deckview/proguard-rules.pro",
"chars": 700,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in C:"
},
{
"path": "deckview/src/androidTest/java/com/appeaser/deckview/ApplicationTest.java",
"chars": 352,
"preview": "package com.appeaser.deckview;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href"
},
{
"path": "deckview/src/main/AndroidManifest.xml",
"chars": 234,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.appeaser.deckview\">\n\n <applicat"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/helpers/DeckChildViewTransform.java",
"chars": 4518,
"preview": "package com.appeaser.deckview.helpers;\n\nimport android.animation.ValueAnimator;\nimport android.graphics.Rect;\nimport and"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/helpers/DeckViewConfig.java",
"chars": 11806,
"preview": "package com.appeaser.deckview.helpers;\n\n/**\n * Created by Vikram on 02/04/2015.\n */\n\nimport android.content.Context;\nimp"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/helpers/DeckViewSwipeHelper.java",
"chars": 13845,
"preview": "package com.appeaser.deckview.helpers;\n\n/**\n * Created by Vikram on 02/04/2015.\n */\n\nimport android.animation.Animator;\n"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/helpers/FakeShadowDrawable.java",
"chars": 10421,
"preview": "package com.appeaser.deckview.helpers;\n\n/**\n * Created by Vikram on 01/04/2015.\n */\n\nimport android.content.res.Resource"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/utilities/DVConstants.java",
"chars": 2793,
"preview": "package com.appeaser.deckview.utilities;\n\n/**\n * Created by Vikram on 02/04/2015.\n */\npublic class DVConstants {\n pub"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/utilities/DVUtils.java",
"chars": 11194,
"preview": "package com.appeaser.deckview.utilities;\n\nimport android.animation.Animator;\nimport android.graphics.Color;\nimport andro"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/utilities/DozeTrigger.java",
"chars": 2025,
"preview": "package com.appeaser.deckview.utilities;\n\n/**\n * Created by Vikram on 01/04/2015.\n */\n\nimport android.os.Handler;\n\n/**\n "
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/utilities/ReferenceCountedTrigger.java",
"chars": 3585,
"preview": "package com.appeaser.deckview.utilities;\n\n/**\n * Created by Vikram on 01/04/2015.\n */\n\nimport android.animation.Animator"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/views/AnimateableDeckChildViewBounds.java",
"chars": 2301,
"preview": "package com.appeaser.deckview.views;\n\nimport android.graphics.Outline;\nimport android.graphics.Rect;\nimport android.view"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/views/DeckChildView.java",
"chars": 24890,
"preview": "package com.appeaser.deckview.views;\n\nimport android.animation.Animator;\nimport android.animation.ObjectAnimator;\nimport"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/views/DeckChildViewHeader.java",
"chars": 14369,
"preview": "package com.appeaser.deckview.views;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapte"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/views/DeckChildViewThumbnail.java",
"chars": 9242,
"preview": "package com.appeaser.deckview.views;\n\n/**\n * Created by Vikram on 02/04/2015.\n */\n\nimport android.animation.Animator;\nim"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/views/DeckView.java",
"chars": 44668,
"preview": "package com.appeaser.deckview.views;\n\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport and"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/views/DeckViewLayoutAlgorithm.java",
"chars": 16112,
"preview": "package com.appeaser.deckview.views;\n\nimport android.graphics.Rect;\n\nimport com.appeaser.deckview.helpers.DeckChildViewT"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/views/DeckViewScroller.java",
"chars": 6945,
"preview": "package com.appeaser.deckview.views;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapte"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/views/DeckViewTouchHandler.java",
"chars": 16858,
"preview": "package com.appeaser.deckview.views;\n\nimport android.content.Context;\nimport android.view.InputDevice;\nimport android.vi"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/views/FixedSizeImageView.java",
"chars": 1762,
"preview": "package com.appeaser.deckview.views;\n\n/**\n * Created by Vikram on 01/04/2015.\n */\n\nimport android.content.Context;\nimpor"
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/views/ViewAnimation.java",
"chars": 2191,
"preview": "package com.appeaser.deckview.views;\n\nimport android.animation.ValueAnimator;\nimport android.graphics.Rect;\n\nimport com."
},
{
"path": "deckview/src/main/java/com/appeaser/deckview/views/ViewPool.java",
"chars": 2250,
"preview": "package com.appeaser.deckview.views;\n\nimport android.content.Context;\n\nimport java.util.Iterator;\nimport java.util.Linke"
},
{
"path": "deckview/src/main/res/drawable-v21/deck_child_view_button_bg.xml",
"chars": 776,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!-- Copyright (C) 2014 The Android Open Source Project\n\n Licensed under the A"
},
{
"path": "deckview/src/main/res/drawable-v21/deck_child_view_dismiss_dark.xml",
"chars": 1143,
"preview": "<!--\nCopyright (C) 2014 The Android Open Source Project\n\n Licensed under the Apache License, Version 2.0 (the \"License"
},
{
"path": "deckview/src/main/res/drawable-v21/deck_child_view_dismiss_light.xml",
"chars": 1143,
"preview": "<!--\nCopyright (C) 2014 The Android Open Source Project\n\n Licensed under the Apache License, Version 2.0 (the \"License"
},
{
"path": "deckview/src/main/res/drawable-v21/deck_child_view_header_bg.xml",
"chars": 862,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!-- Copyright (C) 2014 The Android Open Source Project\n\n Licensed under the A"
},
{
"path": "deckview/src/main/res/drawable-v21/deck_child_view_header_bg_color.xml",
"chars": 904,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!-- Copyright (C) 2014 The Android Open Source Project\n\n Licensed under the A"
},
{
"path": "deckview/src/main/res/interpolator-v21/decelerate_quint.xml",
"chars": 781,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n/*\n** Copyright 2010, The Android Open Source Project\n**\n** Licensed under th"
},
{
"path": "deckview/src/main/res/interpolator-v21/fast_out_linear_in.xml",
"chars": 848,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ~ Copyright (C) 2014 The Android Open Source Project\n ~\n ~ Licensed under"
},
{
"path": "deckview/src/main/res/interpolator-v21/fast_out_slow_in.xml",
"chars": 850,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ~ Copyright (C) 2014 The Android Open Source Project\n ~\n ~ Licensed under"
},
{
"path": "deckview/src/main/res/interpolator-v21/linear_out_slow_in.xml",
"chars": 848,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ~ Copyright (C) 2014 The Android Open Source Project\n ~\n ~ Licensed under"
},
{
"path": "deckview/src/main/res/layout/deck_child_view.xml",
"chars": 1373,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!-- Copyright (C) 2014 The Android Open Source Project\n\n Licensed under the A"
},
{
"path": "deckview/src/main/res/layout/deck_child_view_header.xml",
"chars": 2513,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!-- Copyright (C) 2014 The Android Open Source Project\n\n Licensed under the A"
},
{
"path": "deckview/src/main/res/values/colors.xml",
"chars": 904,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n <!-- The default recents task bar background color. -->\n <col"
},
{
"path": "deckview/src/main/res/values/config.xml",
"chars": 3359,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n <!-- The min animation duration for animating views that are cur"
},
{
"path": "deckview/src/main/res/values/dimens.xml",
"chars": 2202,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n <!-- Used to calculate the translation animation duration, the e"
},
{
"path": "deckview/src/main/res/values/strings.xml",
"chars": 428,
"preview": "<resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n <string name=\"app_name\">DeckViewLibrary</string>\n\n "
},
{
"path": "deckview/src/main/res/values-land/dimens.xml",
"chars": 225,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n <!-- The side padding for the deck as a percentage of the width."
},
{
"path": "deckview/src/main/res/values-sw600dp/dimens.xml",
"chars": 226,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n <!-- The side padding for the deck as a percentage of the width."
},
{
"path": "deckview/src/main/res/values-sw600dp-land/dimens.xml",
"chars": 225,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n <!-- The side padding for the deck as a percentage of the width."
},
{
"path": "deckview/src/main/res/values-sw720dp/dimens.xml",
"chars": 191,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n <!-- The radius of the rounded corners on a task view. -->\n <"
},
{
"path": "deckviewsample/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "deckviewsample/build.gradle",
"chars": 621,
"preview": "apply plugin: 'com.android.application'\n\nandroid {\n compileSdkVersion 22\n buildToolsVersion \"22.0.1\"\n\n defaultC"
},
{
"path": "deckviewsample/proguard-rules.pro",
"chars": 700,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in C:"
},
{
"path": "deckviewsample/src/androidTest/java/com/appeaser/deckviewsample/ApplicationTest.java",
"chars": 358,
"preview": "package com.appeaser.deckviewsample;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <"
},
{
"path": "deckviewsample/src/main/AndroidManifest.xml",
"chars": 767,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package="
},
{
"path": "deckviewsample/src/main/java/com/appeaser/deckviewsample/Datum.java",
"chars": 1573,
"preview": "package com.appeaser.deckviewsample;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport com.squareup.picas"
},
{
"path": "deckviewsample/src/main/java/com/appeaser/deckviewsample/DeckViewSampleActivity.java",
"chars": 8501,
"preview": "package com.appeaser.deckviewsample;\n\nimport android.app.Activity;\nimport android.graphics.Bitmap;\nimport android.graphi"
},
{
"path": "deckviewsample/src/main/res/drawable-v21/box.xml",
"chars": 1027,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n/* //device/apps/common/res/drawable/box.xml\n**\n** Copyright 2007, The Androi"
},
{
"path": "deckviewsample/src/main/res/layout/activity_deck_view_sample.xml",
"chars": 434,
"preview": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:tools=\"http://schemas.android.com/tool"
},
{
"path": "deckviewsample/src/main/res/menu/menu_deck_view_sample.xml",
"chars": 418,
"preview": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:tools=\"http://schemas.android.com/tools\"\n "
},
{
"path": "deckviewsample/src/main/res/values/dimens.xml",
"chars": 211,
"preview": "<resources>\n <!-- Default screen margins, per the Android Design guidelines. -->\n <dimen name=\"activity_horizontal"
},
{
"path": "deckviewsample/src/main/res/values/strings.xml",
"chars": 184,
"preview": "<resources>\n <string name=\"app_name\">DeckViewSample</string>\n\n <string name=\"hello_world\">Hello world!</string>\n "
},
{
"path": "deckviewsample/src/main/res/values/styles.xml",
"chars": 197,
"preview": "<resources>\n\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"android:Theme.Holo.Light.DarkAction"
},
{
"path": "deckviewsample/src/main/res/values-v21/styles.xml",
"chars": 139,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n <style name=\"AppTheme\" parent=\"android:Theme.Material.Light\"></s"
},
{
"path": "deckviewsample/src/main/res/values-w820dp/dimens.xml",
"chars": 358,
"preview": "<resources>\n <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n (such as s"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 232,
"preview": "#Wed Apr 10 15:27:10 PDT 2013\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "gradle.properties",
"chars": 855,
"preview": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will o"
},
{
"path": "gradlew",
"chars": 5080,
"preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n## Gradle start "
},
{
"path": "gradlew.bat",
"chars": 2314,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
},
{
"path": "settings.gradle",
"chars": 47,
"preview": "include ':app', ':deckview', ':deckviewsample'\n"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the vikramkakkar/DeckView GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 74 files (247.1 KB), approximately 59.1k tokens, and a symbol index with 411 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.