Showing preview only (681K chars total). Download the full file or copy to clipboard to get everything.
Repository: facebook/FBNotifications
Branch: master
Commit: 672f61c935e8
Files: 195
Total size: 616.0 KB
Directory structure:
gitextract_ihx0v_r0/
├── .gitignore
├── .gitmodules
├── .travis.yml
├── Android/
│ ├── .gitignore
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradle.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── notifications/
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src/
│ │ └── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── com/
│ │ └── facebook/
│ │ └── notifications/
│ │ ├── NotificationCardResult.java
│ │ ├── NotificationsManager.java
│ │ └── internal/
│ │ ├── activity/
│ │ │ └── CardActivity.java
│ │ ├── appevents/
│ │ │ └── AppEventsLogger.java
│ │ ├── asset/
│ │ │ ├── Asset.java
│ │ │ ├── AssetManager.java
│ │ │ ├── ParcelableAssetHandler.java
│ │ │ ├── cache/
│ │ │ │ ├── CacheOperation.java
│ │ │ │ ├── ContentCache.java
│ │ │ │ ├── ContentDownloader.java
│ │ │ │ └── DiskCache.java
│ │ │ └── handlers/
│ │ │ ├── BitmapAssetHandler.java
│ │ │ ├── ColorAssetHandler.java
│ │ │ └── GifAssetHandler.java
│ │ ├── configuration/
│ │ │ ├── ActionConfiguration.java
│ │ │ ├── ActionsConfiguration.java
│ │ │ ├── BodyConfiguration.java
│ │ │ ├── CardConfiguration.java
│ │ │ └── HeroConfiguration.java
│ │ ├── content/
│ │ │ ├── Content.java
│ │ │ ├── ContentManager.java
│ │ │ └── TextContent.java
│ │ ├── utilities/
│ │ │ ├── EnumCreator.java
│ │ │ ├── FontUtilities.java
│ │ │ ├── GifDecoder.java
│ │ │ ├── InvalidParcelException.java
│ │ │ ├── JSONObjectVisitor.java
│ │ │ ├── RoundedViewHelper.java
│ │ │ ├── TransparentStateListDrawable.java
│ │ │ └── Version.java
│ │ └── view/
│ │ ├── ActionButton.java
│ │ ├── ActionsView.java
│ │ ├── AssetView.java
│ │ ├── BodyView.java
│ │ ├── CardView.java
│ │ ├── ContentView.java
│ │ ├── GifView.java
│ │ └── HeroView.java
│ ├── notifications-example/
│ │ ├── build.gradle
│ │ ├── google-services.json
│ │ ├── proguard-rules.pro
│ │ └── src/
│ │ └── main/
│ │ ├── AndroidManifest.xml
│ │ ├── assets/
│ │ │ ├── example1.json
│ │ │ ├── example2.json
│ │ │ ├── example3.json
│ │ │ ├── example4.json
│ │ │ └── example5.json
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── facebook/
│ │ │ └── notifications/
│ │ │ └── sample/
│ │ │ ├── MainActivity.java
│ │ │ ├── MyGcmListenerService.java
│ │ │ ├── MyInstanceIDListenerService.java
│ │ │ └── RegistrationIntentService.java
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ └── content_main.xml
│ │ ├── menu/
│ │ │ └── menu_main.xml
│ │ ├── values/
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── values-v21/
│ │ │ └── styles.xml
│ │ └── values-w820dp/
│ │ └── dimens.xml
│ └── settings.gradle
├── CHANGELOG-Android.md
├── CHANGELOG-iOS.md
├── CONTRIBUTING.md
├── FBNotifications.podspec
├── Format/
│ ├── Format-1.0.md
│ └── Format-Changelog.md
├── LICENSE
├── LICENSE-specification
├── README.md
└── iOS/
├── .gitignore
├── FBNotifications/
│ ├── Configurations/
│ │ ├── FBNotifications-iOS-Dynamic.xcconfig
│ │ └── FBNotifications-iOS.xcconfig
│ ├── FBNotifications/
│ │ ├── FBNCardViewController.h
│ │ ├── FBNCardViewController.m
│ │ ├── FBNConstants.h
│ │ ├── FBNConstants.m
│ │ ├── FBNotifications.h
│ │ ├── FBNotificationsManager.h
│ │ ├── FBNotificationsManager.m
│ │ ├── Internal/
│ │ │ ├── AppEvents/
│ │ │ │ ├── FBNCardAppEventsLogger.h
│ │ │ │ └── FBNCardAppEventsLogger.m
│ │ │ ├── Asset/
│ │ │ │ ├── Cache/
│ │ │ │ │ ├── FBNAssetContentCache.h
│ │ │ │ │ ├── FBNAssetContentCache.m
│ │ │ │ │ ├── FBNAssetContentCacheOperation.h
│ │ │ │ │ └── FBNAssetContentCacheOperation.m
│ │ │ │ ├── Color/
│ │ │ │ │ ├── FBNColorAsset.h
│ │ │ │ │ ├── FBNColorAsset.m
│ │ │ │ │ ├── FBNColorAssetController.h
│ │ │ │ │ ├── FBNColorAssetController.m
│ │ │ │ │ ├── FBNColorAssetViewController.h
│ │ │ │ │ └── FBNColorAssetViewController.m
│ │ │ │ ├── Common/
│ │ │ │ │ ├── FBNAsset.h
│ │ │ │ │ └── FBNAssetController.h
│ │ │ │ ├── FBNAssetsController.h
│ │ │ │ ├── FBNAssetsController.m
│ │ │ │ ├── GIF/
│ │ │ │ │ ├── FBNGIFAsset.h
│ │ │ │ │ ├── FBNGIFAsset.m
│ │ │ │ │ ├── FBNGIFAssetController.h
│ │ │ │ │ ├── FBNGIFAssetController.m
│ │ │ │ │ ├── FBNIGIFAssetViewController.h
│ │ │ │ │ └── FBNIGIFAssetViewController.m
│ │ │ │ └── Image/
│ │ │ │ ├── FBNImageAsset.h
│ │ │ │ ├── FBNImageAsset.m
│ │ │ │ ├── FBNImageAssetController.h
│ │ │ │ ├── FBNImageAssetController.m
│ │ │ │ ├── FBNImageAssetViewController.h
│ │ │ │ └── FBNImageAssetViewController.m
│ │ │ ├── FBNCardViewController_Internal.h
│ │ │ ├── Models/
│ │ │ │ ├── Actions/
│ │ │ │ │ ├── FBNCardActionConfiguration.h
│ │ │ │ │ ├── FBNCardActionConfiguration.m
│ │ │ │ │ ├── FBNCardActionsConfiguration.h
│ │ │ │ │ ├── FBNCardActionsConfiguration.m
│ │ │ │ │ ├── FBNCardActionsStyle.h
│ │ │ │ │ ├── FBNCardActionsStyle.m
│ │ │ │ │ └── FBNCardButtonAction.h
│ │ │ │ ├── Body/
│ │ │ │ │ ├── FBNCardBodyConfiguration.h
│ │ │ │ │ └── FBNCardBodyConfiguration.m
│ │ │ │ ├── Card/
│ │ │ │ │ ├── FBNCardConfiguration.h
│ │ │ │ │ ├── FBNCardConfiguration.m
│ │ │ │ │ ├── FBNCardDisplayOptions.h
│ │ │ │ │ ├── FBNCardDisplayOptions.m
│ │ │ │ │ ├── FBNCardSize.h
│ │ │ │ │ └── FBNCardSize.m
│ │ │ │ ├── Components/
│ │ │ │ │ ├── FBNAnimatedImage.h
│ │ │ │ │ ├── FBNAnimatedImage.m
│ │ │ │ │ ├── FBNCardBackground.h
│ │ │ │ │ ├── FBNCardBackground.m
│ │ │ │ │ ├── FBNCardColor.h
│ │ │ │ │ ├── FBNCardColor.m
│ │ │ │ │ ├── FBNCardFont.h
│ │ │ │ │ ├── FBNCardFont.m
│ │ │ │ │ ├── FBNCardTextAlignment.h
│ │ │ │ │ ├── FBNCardTextAlignment.m
│ │ │ │ │ ├── FBNCardTextContent.h
│ │ │ │ │ └── FBNCardTextContent.m
│ │ │ │ └── Hero/
│ │ │ │ ├── FBNCardHeroConfiguration.h
│ │ │ │ └── FBNCardHeroConfiguration.m
│ │ │ ├── Utilities/
│ │ │ │ ├── FBNCardError.h
│ │ │ │ ├── FBNCardError.m
│ │ │ │ ├── FBNCardHash.h
│ │ │ │ ├── FBNCardHash.m
│ │ │ │ ├── FBNCardPayload.h
│ │ │ │ ├── FBNCardPayload.m
│ │ │ │ ├── FBNCardViewUtilities.h
│ │ │ │ ├── FBNCardViewUtilities.m
│ │ │ │ └── FBNContentSizeProvider.h
│ │ │ └── Views/
│ │ │ ├── Components/
│ │ │ │ ├── FBNCardDismissButton.h
│ │ │ │ ├── FBNCardDismissButton.m
│ │ │ │ ├── FBNCardLabel.h
│ │ │ │ └── FBNCardLabel.m
│ │ │ ├── FBNCardActionButton.h
│ │ │ ├── FBNCardActionButton.m
│ │ │ ├── FBNCardActionsViewController.h
│ │ │ ├── FBNCardActionsViewController.m
│ │ │ ├── FBNCardBodyViewController.h
│ │ │ ├── FBNCardBodyViewController.m
│ │ │ ├── FBNCardHeroViewController.h
│ │ │ └── FBNCardHeroViewController.m
│ │ └── Resources/
│ │ └── Info.plist
│ └── FBNotifications.xcodeproj/
│ ├── project.pbxproj
│ └── xcshareddata/
│ └── xcschemes/
│ ├── FBNotifications-iOS-Dynamic.xcscheme
│ └── FBNotifications-iOS.xcscheme
├── FBNotifications.xcworkspace/
│ └── contents.xcworkspacedata
├── FBNotificationsExample/
│ ├── .gitignore
│ ├── FBNotificationsExample/
│ │ ├── Resources/
│ │ │ ├── Assets.xcassets/
│ │ │ │ └── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ ├── Base.lproj/
│ │ │ │ ├── LaunchScreen.storyboard
│ │ │ │ └── Main.storyboard
│ │ │ ├── Examples/
│ │ │ │ ├── example1.json
│ │ │ │ ├── example2.json
│ │ │ │ ├── example3.json
│ │ │ │ ├── example4.json
│ │ │ │ └── example5.json
│ │ │ └── Info.plist
│ │ └── Source/
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── ViewController.h
│ │ ├── ViewController.m
│ │ └── main.m
│ └── FBNotificationsExample.xcodeproj/
│ ├── project.pbxproj
│ └── xcshareddata/
│ └── xcschemes/
│ └── FBNotificationsExample.xcscheme
└── Rakefile
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
## OS X
.DS_Store
================================================
FILE: .gitmodules
================================================
[submodule "iOS/Vendor/xctoolchain"]
path = iOS/Vendor/xctoolchain
url = https://github.com/ParsePlatform/xctoolchain.git
================================================
FILE: .travis.yml
================================================
branches:
only:
- master
matrix:
include:
- language: objective-c
os: osx
osx_image: xcode7.3
env: TEST_TYPE=iOS
- language: objective-c
os: osx
osx_image: xcode7.3
env: TEST_TYPE=CocoaPods
- language: objective-c
os: osx
osx_image: xcode7.3
env: TEST_TYPE=Carthage
- language: android
sudo: false
env: TEST_TYPE=Android
android:
components:
- tools
- build-tools-23.0.3
- android-23
- doc-23
- extra-android-support
- extra-android-m2repository
- extra-google-m2repository
- extra-google-google_play_services
install:
- |
if [ "$TEST_TYPE" = "iOS" ]; then
gem install xcpretty -N --no-ri --no-rdoc
elif [ "$TEST_TYPE" = Carthage ]; then
brew install carthage || brew upgrade carthage
fi
script:
- |
if [ "$TEST_TYPE" = "iOS" ]; then
cd iOS
set -o pipefail
xcodebuild build -workspace FBNotifications.xcworkspace -scheme FBNotifications-iOS -sdk iphonesimulator -configuration Release | xcpretty -c
xcodebuild build -workspace FBNotifications.xcworkspace -scheme FBNotificationsExample -sdk iphonesimulator -configuration Release | xcpretty -c
elif [ "$TEST_TYPE" = Android ]; then
cd Android
./gradlew clean assemble -p notifications
elif [ "$TEST_TYPE" = CocoaPods ]; then
pod lib lint FBNotifications.podspec
pod lib lint --use-libraries FBNotifications.podspec
elif [ "$TEST_TYPE" = Carthage ]; then
carthage build --no-skip-current
fi
================================================
FILE: Android/.gitignore
================================================
# built application files
*.apk
*.ap_
# files for the dex VM
*.dex
# Java class files
*.class
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties
# Android Studio
.idea
*.iml
*.ipr
*.iws
classes
gen-external-apklibs
# Gradle
.gradle
build
# Other
.metadata
*/bin/*
*/gen/*
================================================
FILE: Android/build.gradle
================================================
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
classpath 'com.google.gms:google-services:1.3.0'
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: Android/gradle/wrapper/gradle-wrapper.properties
================================================
#Mon Feb 08 14:52:05 PST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
================================================
FILE: Android/gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: Android/gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: Android/gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: Android/notifications/build.gradle
================================================
apply plugin: 'com.android.library'
group = 'com.facebook.android'
buildscript {
repositories {
mavenCentral()
}
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName project.version
}
}
configurations {
javadoc
}
dependencies {
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.2.1'
javadoc 'com.android.support:appcompat-v7:23.2.1'
}
apply plugin: 'maven'
apply plugin: 'signing'
def isSnapshot = version.endsWith('-SNAPSHOT')
def ossrhUsername = hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : System.getenv('CI_NEXUS_USERNAME')
def ossrhPassword = hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : System.getenv('CI_NEXUS_PASSWORD')
task setVersion {
// The version will be derived from source
project.version = null
def sdkVersionFile = file('src/main/java/com/facebook/notifications/NotificationsManager.java')
sdkVersionFile.eachLine{
def matcher = (it =~ /(?:.*LIBRARY_VERSION = \")(.*)(?:\".*)/)
if (matcher.matches()) {
project.version = matcher[0][1]
return
}
}
if (project.version.is('unspecified')) {
throw new GradleScriptException('Version could not be found.', null)
}
}
uploadArchives {
repositories.mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
pom.project {
name 'In-App Notifications Framework'
artifactId = 'notifications'
packaging 'aar'
description 'Facebook In-App Notifications Framework for Android'
url 'https://github.com/facebook/FBNotifications'
scm {
connection 'scm:git@github.com:facebook/FBNotifications.git'
developerConnection 'scm:git@github.com:facebook/FBNotifications.git'
url 'https://github.com/facebook/FBNotifications'
}
licenses {
license {
name 'Facebook Platform License'
url 'https://github.com/facebook/FBNotifications/blob/master/LICENSE'
distribution 'repo'
}
}
developers {
developer {
id 'facebook'
name 'Facebook'
}
}
}
}
}
uploadArchives.dependsOn(setVersion)
signing {
required { !isSnapshot && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
classpath += configurations.javadoc
options.linksOffline("http://d.android.com/reference", "${android.sdkDirectory}/docs/reference")
// JDK 1.8 is more strict then 1.7. Have JDK 1.8 behave like 1.7 for javadoc generation
if (org.gradle.internal.jvm.Jvm.current().getJavaVersion() == JavaVersion.VERSION_1_8) {
options.addStringOption('Xdoclint:none', '-quiet')
}
exclude '**/internal/**'
}
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
classifier = 'javadoc'
from androidJavadocs.destinationDir
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.sourceFiles
}
artifacts {
archives androidSourcesJar
archives androidJavadocsJar
}
================================================
FILE: Android/notifications/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/richardross/Library/Android/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: Android/notifications/src/main/AndroidManifest.xml
================================================
<manifest package="com.facebook.notifications"
xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity android:name=".internal.activity.CardActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:configChanges="orientation|screenSize" />
</application>
</manifest>
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/NotificationCardResult.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
public class NotificationCardResult implements Parcelable {
public static final Creator<NotificationCardResult> CREATOR = new Creator<NotificationCardResult>() {
@Override
public NotificationCardResult createFromParcel(Parcel source) {
return new NotificationCardResult(source);
}
@Override
public NotificationCardResult[] newArray(int size) {
return new NotificationCardResult[size];
}
};
private final @Nullable Uri actionUri;
public NotificationCardResult(@Nullable Uri actionUri) {
this.actionUri = actionUri;
}
private NotificationCardResult(Parcel parcel) {
this.actionUri = parcel.readParcelable(getClass().getClassLoader());
}
@Nullable
public Uri getActionUri() {
return actionUri;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(actionUri, flags);
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/NotificationsManager.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.facebook.notifications.internal.activity.CardActivity;
import com.facebook.notifications.internal.appevents.AppEventsLogger;
import com.facebook.notifications.internal.asset.Asset;
import com.facebook.notifications.internal.asset.AssetManager;
import com.facebook.notifications.internal.asset.handlers.BitmapAssetHandler;
import com.facebook.notifications.internal.asset.handlers.ColorAssetHandler;
import com.facebook.notifications.internal.asset.handlers.GifAssetHandler;
import com.facebook.notifications.internal.configuration.CardConfiguration;
import com.facebook.notifications.internal.content.ContentManager;
import com.facebook.notifications.internal.utilities.Version;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Manages incoming remote notifications for push card presentation.
*/
public final class NotificationsManager {
/**
* Represents handlers to be invoked when preparation of a card is completed.
*/
public interface PrepareCallback {
void onPrepared(@NonNull Intent presentationIntent);
void onError(@NonNull Exception exception);
}
/**
* Allows for customizing of notifications before they are displayed.
*/
public interface NotificationExtender {
Notification.Builder extendNotification(@NonNull Notification.Builder notification);
}
/**
* The request code to use for intents returned by {@link PrepareCallback}
*/
public static final int REQUEST_CODE = 0xCA4D; // CARD
/**
* The intent extra key to be used for pending intents used in notifications created by
* `presentNotification()`
*/
public static final String EXTRA_PAYLOAD_INTENT = "notification_push_payload_intent";
/**
* The highest supported payload version by this version of the Notifications SDK
*/
public static final String PAYLOAD_VERSION = "1.0";
/**
* The version of the In-App Notifications Library
*/
public static final String LIBRARY_VERSION = "1.0.2";
private static final @NonNull Version PAYLOAD_VERSION_OBJECT = new Version(1, 0, 0);
private static final String LOG_TAG = NotificationsManager.class.getCanonicalName();
private static final String NOTIFICATION_TAG = "fb_notification_tag";
private static final String PUSH_PAYLOAD_KEY = "fb_push_payload";
private static final String CARD_PAYLOAD_KEY = "fb_push_card";
private static final AssetManager ASSET_MANAGER = new AssetManager();
private static final ContentManager CONTENT_MANAGER = new ContentManager();
static {
ASSET_MANAGER.registerHandler(BitmapAssetHandler.TYPE, new BitmapAssetHandler());
ASSET_MANAGER.registerHandler(ColorAssetHandler.TYPE, new ColorAssetHandler());
ASSET_MANAGER.registerHandler(GifAssetHandler.TYPE, new GifAssetHandler());
}
private NotificationsManager() {
}
@Nullable
private static JSONObject getPushJSON(@NonNull Bundle bundle) throws JSONException {
String pushPayload = bundle.getString(PUSH_PAYLOAD_KEY);
if (pushPayload == null) {
return null;
}
return new JSONObject(pushPayload);
}
@Nullable
private static JSONObject getCardJSON(@NonNull Bundle bundle) throws JSONException {
String cardPayload = bundle.getString(CARD_PAYLOAD_KEY);
if (cardPayload == null) {
return null;
}
return new JSONObject(cardPayload);
}
@Nullable
private static Intent intentForBundle(
@NonNull Context context,
@Nullable JSONObject pushJSON,
@NonNull JSONObject cardJSON,
@NonNull AssetManager assetManager,
@NonNull ContentManager contentManager
) throws JSONException {
Version cardVersion = Version.parse(cardJSON.optString("version"));
if (cardVersion == null || cardVersion.compareTo(PAYLOAD_VERSION_OBJECT) > 0) {
return null;
}
Intent intent = new Intent(context, CardActivity.class);
if (pushJSON != null) {
String campaignIdentifier = AppEventsLogger.getCampaignIdentifier(pushJSON);
if (campaignIdentifier != null) {
intent.putExtra(CardActivity.EXTRA_CAMPAIGN_IDENTIFIER, campaignIdentifier);
}
}
intent.putExtra(CardActivity.EXTRA_ASSET_MANAGER, assetManager);
intent.putExtra(CardActivity.EXTRA_CONTENT_MANAGER, contentManager);
intent.putExtra(CardActivity.EXTRA_CARD_PAYLOAD, cardJSON.toString());
return intent;
}
@NonNull
private static AssetManager getAssetManager(@NonNull Context context) {
AssetManager manager = new AssetManager(ASSET_MANAGER);
manager.setContext(context);
return manager;
}
@NonNull
private static ContentManager getContentManager(@NonNull Context context) {
ContentManager manager = new ContentManager(CONTENT_MANAGER);
manager.setContext(context);
return manager;
}
/**
* Returns whether or not a notification bundle has a valid push payload.
*
* @param notificationBundle The bundle to check for a push payload
*/
public static boolean canPresentCard(@NonNull Bundle notificationBundle) {
return notificationBundle.containsKey(CARD_PAYLOAD_KEY);
}
/**
* Present an intent from a given activity to show the notification bundle contained in notificationBundle.
*
* @param activity The activity to present from
* @param notificationBundle The bundle containing the notification payload to present
* @return whether or not the activity could successfully be presented
*/
public static boolean presentCard(@NonNull Activity activity, @NonNull Bundle notificationBundle) {
try {
JSONObject cardJSON = getCardJSON(notificationBundle);
if (cardJSON == null) {
return false;
}
JSONObject pushJSON = getPushJSON(notificationBundle);
Intent presentationIntent = intentForBundle(activity,
pushJSON, cardJSON,
getAssetManager(activity), getContentManager(activity)
);
if (presentationIntent == null) {
return false;
}
activity.startActivityForResult(presentationIntent, REQUEST_CODE);
return true;
} catch (JSONException ex) {
Log.e(LOG_TAG, "Error while parsing JSON", ex);
return false;
}
}
/**
* Prepare and pre-load a notification bundle into memory.
*
* @param context The current context of your program. Usually an activity, application, or
* service context.
* @param notificationBundle The bundle containing the notification payload to present
* @param callback The callback to invoke once preparation is complete. This is guaranteed to be
* invoked on the same thread as this method is invoked from.
*/
public static void prepareCard(
@NonNull final Context context,
@NonNull final Bundle notificationBundle,
@NonNull final PrepareCallback callback
) {
final Handler handler = new Handler();
final AssetManager assetManager = getAssetManager(context);
final ContentManager contentManager = getContentManager(context);
// Cache and prepare in background.
new Thread() {
@Override
public void run() {
try {
JSONObject cardJSON = getCardJSON(notificationBundle);
if (cardJSON == null) {
throw new NullPointerException("No content present in the notification bundle.");
}
Version cardVersion = Version.parse(cardJSON.optString("version"));
if (cardVersion == null || cardVersion.compareTo(PAYLOAD_VERSION_OBJECT) > 0) {
throw new Exception("Payload version " + cardVersion + " not supported by this version of the notifications SDK.");
}
assetManager.cachePayload(cardJSON, new AssetManager.CacheCompletionCallback() {
@Override
public void onCacheCompleted(@NonNull JSONObject payload) {
assetManager.stopCaching();
try {
JSONObject cardJSON = getCardJSON(notificationBundle);
final Intent presentIntent = intentForBundle(context, getPushJSON(notificationBundle), cardJSON, assetManager, contentManager);
if (presentIntent == null) {
throw new NullPointerException("presentIntent was null, this should never happen!");
}
CardConfiguration configuration = new CardConfiguration(cardJSON, assetManager, contentManager);
presentIntent.putExtra(CardActivity.EXTRA_CONFIGURATION, configuration);
handler.post(new Runnable() {
@Override
public void run() {
callback.onPrepared(presentIntent);
}
});
} catch (final Exception ex) {
handler.post(new Runnable() {
@Override
public void run() {
callback.onError(ex);
}
});
}
}
});
} catch (final Exception ex) {
handler.post(new Runnable() {
@Override
public void run() {
callback.onError(ex);
}
});
}
}
}.start();
}
/**
* Handle the result of an activity started using
* {@code prepare(Context, Bundle, PrepareCallback)} or {@code present(Activity, Bundle)}.
*
* @param requestCode The request code used to start the activity
* @param resultCode The result code returned by the activity
* @param data The data returned by the activity
* @return The notification card result of the activity if it exists, or null if it does not.
*/
public static NotificationCardResult handleActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (requestCode != REQUEST_CODE) {
return null;
}
if (resultCode != Activity.RESULT_OK || data == null) {
return null;
}
return data.getParcelableExtra(CardActivity.EXTRA_NOTIFICATION_CARD_RESULT);
}
/**
* Present a {@link Notification} to be presented from a GCM push bundle.
* <p/>
* This does not present a notification immediately, instead it caches the assets from the
* notification bundle, and then presents a notification to the user. This allows for a smoother
* interaction without loading indicators for the user.
* <p/>
* Note that only one notification can be created for a specific push bundle, should you attempt
* to present a new notification with the same payload bundle as an existing notification, it will
* replace and update the old notification.
*
* @param context The context to send the notification from
* @param notificationBundle The content of the push notification
* @param launcherIntent The launcher intent that contains your Application's activity.
* This will be modified with the FLAG_ACTIVITY_CLEAR_TOP and
* FLAG_ACTIVITY_SINGLE_TOP flags, in order to properly show the
* notification in an already running application.
* <p/>
* Should you not want this behavior, you may use the notificationExtender
* parameter to customize the contentIntent of the notification before
* presenting it.
*/
public static boolean presentNotification(
@NonNull Context context,
@NonNull Bundle notificationBundle,
@NonNull Intent launcherIntent) {
return presentNotification(context, notificationBundle, launcherIntent, null);
}
/**
* Present a {@link Notification} to be presented from a GCM push bundle.
* <p/>
* This does not present a notification immediately, instead it caches the assets from the
* notification bundle, and then presents a notification to the user. This allows for a smoother
* interaction without loading indicators for the user.
* <p/>
* Note that only one notification can be created for a specific push bundle, should you attempt
* to present a new notification with the same payload bundle as an existing notification, it will
* replace and update the old notification.
*
* @param context The context to send the notification from
* @param notificationBundle The content of the push notification
* @param launcherIntent The launcher intent that contains your Application's activity.
* This will be modified with the FLAG_ACTIVITY_CLEAR_TOP and
* FLAG_ACTIVITY_SINGLE_TOP flags, in order to properly show the
* notification in an already running application.
* <p/>
* Should you not want this behavior, you may use the notificationExtender
* parameter to customize the contentIntent of the notification before
* presenting it.
* @param notificationExtender A nullable argument that allows you to customize the notification
* before displaying it. Use this to configure Icons, text, sounds,
* etc. before we pass the notification off to the OS.
*/
public static boolean presentNotification(
@NonNull final Context context,
@NonNull final Bundle notificationBundle,
@NonNull final Intent launcherIntent,
@Nullable final NotificationExtender notificationExtender) {
final JSONObject alert;
final int payloadHash;
try {
String payload = notificationBundle.getString(CARD_PAYLOAD_KEY);
if (payload == null) {
return false;
}
payloadHash = payload.hashCode();
JSONObject payloadObject = new JSONObject(payload);
alert = payloadObject.optJSONObject("alert") != null
? payloadObject.optJSONObject("alert")
: new JSONObject();
} catch (JSONException ex) {
Log.e(LOG_TAG, "Error while parsing notification bundle JSON", ex);
return false;
}
final boolean[] success = new boolean[1];
final Thread backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
prepareCard(context, notificationBundle, new PrepareCallback() {
@Override
public void onPrepared(@NonNull Intent presentationIntent) {
Intent contentIntent = new Intent(launcherIntent);
contentIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
contentIntent.putExtra(EXTRA_PAYLOAD_INTENT, presentationIntent);
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setContentTitle(alert.optString("title"))
.setContentText(alert.optString("body"))
.setAutoCancel(true)
.setContentIntent(
PendingIntent.getActivity(
context.getApplicationContext(),
payloadHash,
contentIntent,
PendingIntent.FLAG_ONE_SHOT
)
);
if (notificationExtender != null) {
builder = notificationExtender.extendNotification(builder);
}
manager.notify(NOTIFICATION_TAG, payloadHash, builder.getNotification());
success[0] = true;
Looper.myLooper().quit();
}
@Override
public void onError(@NonNull Exception exception) {
Log.e(LOG_TAG, "Error while preparing card", exception);
Looper.myLooper().quit();
}
});
Looper.loop();
}
});
backgroundThread.start();
try {
backgroundThread.join();
} catch (InterruptedException ex) {
Log.e(LOG_TAG, "Failed to wait for background thread", ex);
return false;
}
return success[0];
}
/**
* Present a card from the notification this activity
* was created from, if the notification exists.
*
* @param activity The activity to present from.
* @return Whether or not a card was presented.
*/
public static boolean presentCardFromNotification(@NonNull Activity activity) {
return presentCardFromNotification(activity, activity.getIntent());
}
/**
* Present a card from the notification this activity
* was relaunched from, if the notification exists.
*
* @param activity The activity to present from.
* @param intent Intent that was used to re-launch the activity.
* @return Whether or not a card was presented.
*/
public static boolean presentCardFromNotification(@NonNull Activity activity,
@NonNull Intent intent) {
Intent notificationIntent = intent.getParcelableExtra(EXTRA_PAYLOAD_INTENT);
if (notificationIntent == null) {
return false;
}
activity.startActivityForResult(notificationIntent, REQUEST_CODE);
return true;
}
/**
* Registers an Asset Handler for use.
*
* @param assetType The type of the asset to register for.
* @param assetHandler The asset handler to register.
*/
private static void registerAssetHandler(
@NonNull String assetType,
@NonNull AssetManager.AssetHandler<? extends Asset> assetHandler
) {
ASSET_MANAGER.registerHandler(assetType, assetHandler);
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/activity/CardActivity.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.activity;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.Gravity;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
import android.widget.ProgressBar;
import com.facebook.notifications.NotificationCardResult;
import com.facebook.notifications.internal.appevents.AppEventsLogger;
import com.facebook.notifications.internal.asset.AssetManager;
import com.facebook.notifications.internal.asset.handlers.ColorAssetHandler;
import com.facebook.notifications.internal.configuration.CardConfiguration;
import com.facebook.notifications.internal.content.ContentManager;
import com.facebook.notifications.internal.view.ActionButton;
import com.facebook.notifications.internal.view.ActionsView;
import com.facebook.notifications.internal.view.CardView;
import org.json.JSONException;
import org.json.JSONObject;
/**
* An activity which displays a push card.
*/
public class CardActivity extends Activity implements ActionsView.Delegate {
/**
* The intent extra key to be set with the JSON payload of your push card payload.
*/
public static final String EXTRA_CARD_PAYLOAD = "fb_push_card_payload";
/**
* If the configuration has already been cached and parsed, you may pass simply the entire
* configuration object to the activity. This is significantly faster than re-parsing the entire
* JSON object.
*/
public static final String EXTRA_CONFIGURATION = "fb_push_card_configuration";
/**
* The intent extra key to be set with the push campaign identifier.
*/
public static final String EXTRA_CAMPAIGN_IDENTIFIER = "fb_push_campaign";
/**
* The intent extra key to be set with an instance of {@link AssetManager}
*/
public static final String EXTRA_ASSET_MANAGER = "fb_push_card_asset_manager";
/**
* The intent extra key to be set with an instance of {@link ContentManager}
*/
public static final String EXTRA_CONTENT_MANAGER = "fb_push_card_content_manager";
/**
* The intent extra key to be set with the card result.
*/
public static final String EXTRA_NOTIFICATION_CARD_RESULT = "fb_notification_card_result";
private static final String LOG_TAG = CardActivity.class.getCanonicalName();
private @Nullable String campaignIdentifier;
private @Nullable JSONObject configurationPayload;
private @NonNull AssetManager assetManager;
private @NonNull ContentManager contentManager;
private @NonNull AppEventsLogger appEventsLogger;
private @Nullable CardView cardView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
appEventsLogger = new AppEventsLogger(this);
Intent intent = getIntent();
campaignIdentifier = intent.getStringExtra(EXTRA_CAMPAIGN_IDENTIFIER);
String payloadString = intent.getStringExtra(EXTRA_CARD_PAYLOAD);
CardConfiguration configuration = intent.getParcelableExtra(EXTRA_CONFIGURATION);
AssetManager assetManager = intent.getParcelableExtra(EXTRA_ASSET_MANAGER);
ContentManager contentManager = intent.getParcelableExtra(EXTRA_CONTENT_MANAGER);
assetManager = assetManager != null ? assetManager : new AssetManager();
contentManager = contentManager != null ? contentManager : new ContentManager();
assetManager.setContext(this);
contentManager.setContext(this);
this.assetManager = assetManager;
this.contentManager = contentManager;
try {
configurationPayload = new JSONObject(payloadString);
} catch (JSONException ex) {
Log.e(LOG_TAG, "Error parsing JSON payload", ex);
}
if (configuration == null) {
beginLoadingContent();
} else {
displayConfiguration(configuration);
}
appEventsLogger.logPushOpen(campaignIdentifier);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (assetManager == null) {
return;
}
assetManager.stopCaching();
if (configurationPayload == null) {
return;
}
assetManager.clearCache(configurationPayload);
}
private void beginLoadingContent() {
if (assetManager == null || contentManager == null) {
Log.e(LOG_TAG, "Asset & content manager should be available!");
return;
}
if (configurationPayload == null) {
Log.e(LOG_TAG, "No card payload is available!");
return;
}
ProgressBar loadingView = new ProgressBar(this);
loadingView.setIndeterminate(true);
int backgroundColor = ColorAssetHandler.fromRGBAHex(configurationPayload.optString("backdropColor"));
FrameLayout loadingViewFrame = new FrameLayout(this);
loadingViewFrame.setBackgroundColor(backgroundColor);
LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.CENTER;
loadingViewFrame.addView(loadingView, layoutParams);
setContentView(loadingViewFrame);
final Handler handler = new Handler();
if (configurationPayload == null) {
return;
}
assetManager.cachePayload(configurationPayload, new AssetManager.CacheCompletionCallback() {
@Override
public void onCacheCompleted(@NonNull JSONObject payload) {
final CardConfiguration configuration;
try {
configuration = new CardConfiguration(configurationPayload, assetManager, contentManager);
} catch (JSONException ex) {
Log.e(LOG_TAG, "Error while parsing JSON", ex);
return;
}
handler.post(new Runnable() {
@Override
public void run() {
displayConfiguration(configuration);
}
});
}
});
}
private void displayConfiguration(CardConfiguration configuration) {
if (assetManager == null || contentManager == null) {
Log.e(LOG_TAG, "Asset or content manager has unloaded since beginLoadingContent()!");
return;
}
cardView = new CardView(this, assetManager, contentManager, this, configuration);
setContentView(cardView);
}
@Override
public void onBackPressed() {
appEventsLogger.logButtonAction(ActionButton.Type.Dismiss, campaignIdentifier);
Intent resultIntent = new Intent();
resultIntent.putExtra(EXTRA_NOTIFICATION_CARD_RESULT, new NotificationCardResult(null));
setResult(RESULT_OK, resultIntent);
finish();
}
@Override
public void actionButtonClicked(ActionButton.Type type, @Nullable Uri actionUri) {
appEventsLogger.logButtonAction(type, campaignIdentifier);
Intent resultIntent = new Intent();
resultIntent.putExtra(EXTRA_NOTIFICATION_CARD_RESULT, new NotificationCardResult(actionUri));
setResult(RESULT_OK, resultIntent);
finish();
if (actionUri == null) {
return;
}
Intent actionIntent = new Intent(Intent.ACTION_VIEW, actionUri);
startActivity(actionIntent);
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/appevents/AppEventsLogger.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.appevents;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.facebook.notifications.internal.view.ActionButton;
import org.json.JSONObject;
import java.lang.reflect.Method;
/**
* Simple class to call into core Facebook SDK and create/invoke the logger from there to report
* App Events that have been triggered by a push card.
*/
public class AppEventsLogger {
private static final String LOG_TAG = AppEventsLogger.class.getCanonicalName();
private static final String PUSH_OPEN_EVENT = "fb_mobile_push_opened";
private static final String PUSH_CAMPAIGN_KEY = "fb_push_campaign";
private @Nullable Object fbSDKLogger;
private @Nullable Method fbSDKLogMethod;
public AppEventsLogger(@NonNull Context context) {
try {
Class<?> loggerClass = Class.forName("com.facebook.appevents.AppEventsLogger");
Method instantiateMethod = loggerClass.getMethod("newLogger", Context.class);
fbSDKLogMethod = loggerClass.getMethod("logEvent", String.class, Bundle.class);
fbSDKLogger = instantiateMethod.invoke(null, context);
} catch (Exception ex) {
Log.w(
LOG_TAG,
"Failed to initialize AppEventsLogger. " +
"Did you forget to include the Facebook SDK in your application?",
ex
);
}
}
@Nullable
public static String getCampaignIdentifier(@NonNull JSONObject payload) {
return payload.optString("campaign", null);
}
@NonNull
private static String getAppEventName(ActionButton.Type action) {
switch (action) {
case Primary:
return "fb_mobile_push_card_action_primary";
case Secondary:
return "fb_mobile_push_card_action_secondary";
case Dismiss:
return "fb_mobile_push_card_action_dismiss";
default:
throw new RuntimeException("Unknown action type: " + action);
}
}
public void logPushOpen(@Nullable String campaignIdentifier) {
logEvent(PUSH_OPEN_EVENT, campaignIdentifier);
}
public void logButtonAction(ActionButton.Type action, @Nullable String campaignIdentifier) {
logEvent(getAppEventName(action), campaignIdentifier);
}
private void logEvent(@NonNull String eventName, @Nullable String campaignIdentifier) {
if (campaignIdentifier == null || campaignIdentifier.equals("") ||
fbSDKLogger == null || fbSDKLogMethod == null) {
return;
}
Bundle parameters = new Bundle();
parameters.putString(PUSH_CAMPAIGN_KEY, campaignIdentifier);
try {
fbSDKLogMethod.invoke(fbSDKLogger, eventName, parameters);
} catch (Exception ex) {
Log.w(
LOG_TAG,
"Failed to invoke AppEventsLogger." +
"Did you forget to include the Facebook SDK in your application?",
ex
);
}
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/Asset.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.asset;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import com.facebook.notifications.internal.utilities.InvalidParcelException;
/**
* A 'marker' interface used for identifying an object as a resource. On its own, doesn't do much.
*/
public interface Asset extends Parcelable {
/**
* Get the type of this Asset. Used by {@link AssetManager} to find the proper
* {@link AssetManager.AssetHandler} for this asset.
*
* @return The asset type of the asset.
*/
@NonNull
String getType();
/**
* Validate that all of the content for this asset has been properly cached and prepared.
*
* @throws InvalidParcelException
*/
void validate() throws InvalidParcelException;
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/AssetManager.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.asset;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import com.facebook.notifications.internal.asset.cache.ContentCache;
import com.facebook.notifications.internal.utilities.InvalidParcelException;
import com.facebook.notifications.internal.utilities.JSONObjectVisitor;
import org.json.JSONObject;
import java.io.File;
import java.net.URL;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Creates different Asset types based on the contents of a JSON payload
*/
public class AssetManager implements Parcelable {
/**
* Allows for the fetching of previously-fetched content from an on-disk cache.
*/
public interface AssetCache {
/**
* Gets the on-disk file for a previously cached URL.
*
* @param url The url to fetch from the cache
* @return The file which contains the contents of the url, or null if the URL was not
* previously cached, or if caching failed
*/
File getCachedFile(URL url);
}
/**
* An interface that allows different types of {@link Asset} to be displayed and used by the push
* card.
*
* This must be either {@link Parcelable} or constructable with a default constructor.
*/
public interface AssetHandler<AssetType extends Asset> {
/**
* Invoked by {@link AssetManager} when an asset is being cached
*
* @param payload The payload to cache
* @return A set of URLs that contains the content to download into the asset cache, or null if
* no URLs exist to cache.
*/
@Nullable
Set<URL> getCacheURLs(@NonNull JSONObject payload);
/**
* Invoked by {@link AssetManager} when an asset should be inflated from a JSON payload
* All of the URLs returned by `getCacheURLs()` are guaranteed to have been fully downloaded
*
* @param payload The payload to inflate from
* @param cache The cache which contains the URLs that have been requested to be downloaded
* @return The fully inflated asset, or null if inflation fails
*/
@Nullable
AssetType createAsset(@NonNull JSONObject payload, @NonNull AssetCache cache);
/**
* Invoked by {@link AssetManager} when an asset from this handler has been requested to be
* displayed
*
* @param asset The asset to create a view for
* @param context The context to create a view for
* @return A new view
*/
@NonNull
View createView(@NonNull AssetType asset, @NonNull Context context);
}
/**
* An interface for receiving a callback when the caching of a set of resources has completed.
*/
public interface CacheCompletionCallback {
/**
* Invoked by the {@link AssetManager} whenever caching has finished.
*
* @param payload The payload which has been cached.
*/
void onCacheCompleted(@NonNull JSONObject payload);
}
public static final Creator<AssetManager> CREATOR = new Creator<AssetManager>() {
@Override
public AssetManager createFromParcel(Parcel source) {
try {
AssetManager assetManager = new AssetManager(source);
assetManager.validate();
return assetManager;
} catch (InvalidParcelException ex) {
Log.w(LOG_TAG, "Failed to decode asset manager", ex);
return null;
}
}
@Override
public AssetManager[] newArray(int size) {
return new AssetManager[size];
}
};
private static final String LOG_TAG = AssetManager.class.getCanonicalName();
private @Nullable ContentCache contentCache;
private final @NonNull Map<String, ParcelableAssetHandler> registeredHandlers;
public AssetManager() {
registeredHandlers = new ConcurrentHashMap<>();
}
/**
* Creates a new AssetManager from another manager's handlers, but without a context set.
* @param other The asset manager to clone
*/
public AssetManager(AssetManager other) {
registeredHandlers = new ConcurrentHashMap<>(other.registeredHandlers);
}
public AssetManager(@NonNull Parcel parcel) {
registeredHandlers = new ConcurrentHashMap<>();
Bundle handlersBundle = parcel.readBundle(getClass().getClassLoader());
for (String type : handlersBundle.keySet()) {
registeredHandlers.put(type, (ParcelableAssetHandler)handlersBundle.getParcelable(type));
}
}
public void setContext(@NonNull Context context) {
if (contentCache != null) {
throw new UnsupportedOperationException("Can only call setContext() once on an AssetManager!");
}
contentCache = new ContentCache(context);
}
private void validate() throws InvalidParcelException {
for (ParcelableAssetHandler handler : registeredHandlers.values()) {
handler.validate();
}
}
public void registerHandler(@NonNull String assetType, @NonNull AssetHandler<? extends Asset> handler) {
registeredHandlers.put(assetType, new ParcelableAssetHandler(handler));
}
@Nullable
public Asset inflateAsset(@Nullable JSONObject payload) {
if (contentCache == null) {
throw new UnsupportedOperationException("Cannot call inflateAsset() before setContext() has been called!");
}
if (payload == null) {
return null;
}
String type = payload.optString("_type");
AssetHandler<? extends Asset> handler = registeredHandlers.get(type);
if (handler == null) {
return null;
}
return handler.createAsset(payload, contentCache);
}
@NonNull
public <AssetType extends Asset> View inflateView(@NonNull AssetType asset, @NonNull Context context) {
String type = asset.getType();
@SuppressWarnings("unchecked")
AssetHandler<AssetType> handler = (AssetHandler<AssetType>) registeredHandlers.get(type);
if (handler == null) {
throw new IllegalArgumentException("Asset type \"" + type + "\" not registered!");
}
return handler.createView(asset, context);
}
/**
* Caches a given JSON payload in the background.
*
* @param payload The payload to cache
* @param callback The callback to be invoked when caching completes.
*/
public void cachePayload(final @NonNull JSONObject payload, final @NonNull CacheCompletionCallback callback) {
if (contentCache == null) {
throw new UnsupportedOperationException("Cannot call cachePayload() before setContext() has been called!");
}
contentCache.cache(getCacheURLs(payload), new ContentCache.CompletionCallback() {
@Override
public void onCacheCompleted(@NonNull Set<URL> urlsToCache) {
callback.onCacheCompleted(payload);
}
});
}
/**
* Clears the cache for a given JSON payload.
*
* @param payload The payload to clear the cache for.
*/
public void clearCache(@NonNull JSONObject payload) {
if (contentCache == null) {
throw new UnsupportedOperationException("Cannot call cachePayload() before setContext() has been called!");
}
contentCache.clear(getCacheURLs(payload));
}
/**
* Stops any caching that may be occurring in the background.
*/
public void stopCaching() {
if (contentCache == null) {
throw new UnsupportedOperationException("Cannot call stopCaching() before setContext() has been called!");
}
contentCache.stop();
}
@NonNull
private Set<URL> getCacheURLs(@Nullable JSONObject payload) {
if (contentCache == null) {
throw new UnsupportedOperationException("Cannot call stopCaching() before setContext() has been called!");
}
if (payload == null) {
return new HashSet<>();
}
final Set<URL> cacheURLs = new HashSet<>();
JSONObjectVisitor.walk(payload, new JSONObjectVisitor() {
@Override
protected void visit(JSONObject object) {
super.visit(object);
String type = object.optString("_type");
AssetHandler<? extends Asset> handler = registeredHandlers.get(type);
if (handler == null) {
return;
}
Set<URL> newURLs = handler.getCacheURLs(object);
if (newURLs == null) {
return;
}
cacheURLs.addAll(newURLs);
}
});
return cacheURLs;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
Bundle bundle = new Bundle();
for (Map.Entry<String, ParcelableAssetHandler> entry : registeredHandlers.entrySet()) {
bundle.putParcelable(entry.getKey(), entry.getValue());
}
dest.writeBundle(bundle);
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/ParcelableAssetHandler.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.asset;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import com.facebook.notifications.internal.utilities.InvalidParcelException;
import org.json.JSONObject;
import java.net.URL;
import java.util.Set;
/**
* Represents a parcelable entry in {@link AssetManager}'s handler list.
*/
class ParcelableAssetHandler implements AssetManager.AssetHandler<Asset>, Parcelable {
public static final Creator<ParcelableAssetHandler> CREATOR = new Creator<ParcelableAssetHandler>() {
@Override
public ParcelableAssetHandler createFromParcel(Parcel source) {
return new ParcelableAssetHandler(source);
}
@Override
public ParcelableAssetHandler[] newArray(int size) {
return new ParcelableAssetHandler[size];
}
};
private final @Nullable AssetManager.AssetHandler<Asset> handler;
private final @Nullable InvalidParcelException exception;
@SuppressWarnings("unchecked")
public ParcelableAssetHandler(@NonNull AssetManager.AssetHandler<? extends Asset> handler) {
this.handler = (AssetManager.AssetHandler) handler;
this.exception = null;
}
@SuppressWarnings("unchecked")
public ParcelableAssetHandler(Parcel source) {
boolean isParcelable = source.readInt() != 0;
if (isParcelable) {
handler = source.readParcelable(getClass().getClassLoader());
exception = null;
} else {
AssetManager.AssetHandler handler = null;
InvalidParcelException exception = null;
try {
Class assetHandlerClass = (Class) Class.forName(source.readString(), true, getClass().getClassLoader());
handler = (AssetManager.AssetHandler) assetHandlerClass.newInstance();
} catch (Exception ex) {
exception = new InvalidParcelException(ex);
}
this.handler = handler;
this.exception = exception;
}
}
public void validate() throws InvalidParcelException {
if (exception != null) {
throw exception;
}
if (handler == null) {
throw new IllegalStateException("AssetHandler should not be null when parceling if no exception was thrown!");
}
}
@Nullable
@Override
public Set<URL> getCacheURLs(@NonNull JSONObject payload) {
if (handler == null) {
throw new IllegalStateException("AssetHandler should not be null, did you forget to call validate()?");
}
return handler.getCacheURLs(payload);
}
@Nullable
@Override
public Asset createAsset(@NonNull JSONObject payload, @NonNull AssetManager.AssetCache cache) {
if (handler == null) {
throw new IllegalStateException("AssetHandler should not be null, did you forget to call validate()?");
}
return handler.createAsset(payload, cache);
}
@NonNull
@Override
public View createView(@NonNull Asset asset, @NonNull Context context) {
if (handler == null) {
throw new IllegalStateException("AssetHandler should not be null, did you forget to call validate()?");
}
return handler.createView(asset, context);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
if (handler == null) {
throw new IllegalStateException("AssetHandler should not be null, did you forget to call validate()?");
}
if (handler instanceof Parcelable) {
dest.writeInt(1);
dest.writeParcelable((Parcelable)handler, flags);
} else {
dest.writeInt(0);
dest.writeString(handler.getClass().getName());
}
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/cache/CacheOperation.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.asset.cache;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.io.File;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
class CacheOperation implements ContentDownloader.DownloadCallback {
private final @NonNull Object mutex;
private final @NonNull Set<URL> urlsToCache;
private final @NonNull Set<URL> remainingURLs;
private final @NonNull ContentCache.CompletionCallback completion;
public CacheOperation(@NonNull Set<URL> urlsToCache, @NonNull ContentCache.CompletionCallback completion) {
this.mutex = new Object();
this.urlsToCache = urlsToCache;
this.remainingURLs = new HashSet<>(urlsToCache);
this.completion = completion;
}
@NonNull
public Set<URL> getUrlsToCache() {
return urlsToCache;
}
@NonNull
public ContentCache.CompletionCallback getCompletion() {
return completion;
}
@Override
public void onResourceDownloaded(@NonNull URL url, @Nullable File targetFile) {
boolean invoke;
synchronized (mutex) {
remainingURLs.remove(url);
invoke = remainingURLs.size() == 0;
}
if (invoke) {
completion.onCacheCompleted(urlsToCache);
}
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/cache/ContentCache.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.asset.cache;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.facebook.notifications.internal.asset.Asset;
import com.facebook.notifications.internal.asset.AssetManager;
import java.io.File;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Manages the caching of an {@link Asset}'s resources
* from the internet
*/
public class ContentCache implements AssetManager.AssetCache {
public interface CompletionCallback {
void onCacheCompleted(@NonNull Set<URL> urlsToCache);
}
private static final String LOG_TAG = ContentCache.class.getCanonicalName();
private final @NonNull Context context;
private final @NonNull ContentDownloader downloader;
private final @NonNull Thread downloadThread;
private final @NonNull DiskCache diskCache;
private final @NonNull Object synchronizationMutex;
private final @NonNull Map<String, Set<CacheOperation>> cacheOperations;
private @Nullable Set<String> cachedKeys;
public ContentCache(@NonNull Context context) {
this.context = context;
downloader = new ContentDownloader();
diskCache = new DiskCache(context);
downloadThread = new Thread(downloader);
synchronizationMutex = new Object();
cacheOperations = new HashMap<>();
downloadThread.start();
}
@NonNull
private static String getCacheKey(@NonNull URL url) {
String urlString = url.toString();
try {
MessageDigest MD5 = MessageDigest.getInstance("MD5");
Charset UTF8 = Charset.forName("UTF-8");
byte[] toDigest = urlString.getBytes(UTF8);
byte[] digested = MD5.digest(toDigest);
return String.format(
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
digested[0], digested[1], digested[2], digested[3], digested[4], digested[5], digested[6],
digested[7], digested[8], digested[9], digested[10], digested[11], digested[12],
digested[13], digested[14], digested[15]);
} catch (NoSuchAlgorithmException ex) {
// If for some ungodly reason MD5 doesn't exist in this JVM, use the string's built-in hash
// code.
return Integer.toHexString(urlString.hashCode());
}
}
public void stop() {
downloadThread.interrupt();
try {
downloadThread.join();
} catch (InterruptedException ex) {
// If we were interrupted while waiting to have our children die, re-interrupt the
// child thread and hope for the best.
downloadThread.interrupt();
}
}
@NonNull
public Context getContext() {
return context;
}
public void cache(@NonNull Set<URL> urlsToCache, @NonNull CompletionCallback completion) {
CacheOperation operation = new CacheOperation(urlsToCache, completion);
synchronized (synchronizationMutex) {
int scheduledCount = 0;
for (Iterator<URL> urlIterator = operation.getUrlsToCache().iterator(); urlIterator.hasNext(); ) {
URL url = urlIterator.next();
final String hashKey = getCacheKey(url);
if (hasCachedData(hashKey)) {
urlIterator.remove();
continue;
}
if (cacheOperations.containsKey(hashKey)) {
Set<CacheOperation> existingOperations = cacheOperations.get(hashKey);
existingOperations.add(operation);
continue;
}
final Set<CacheOperation> newOperations = new HashSet<>();
newOperations.add(operation);
cacheOperations.put(hashKey, newOperations);
downloader.downloadAsync(url, diskCache.fetch(getCacheKey(url)), new ContentDownloader.DownloadCallback() {
@Override
public void onResourceDownloaded(@NonNull URL url, @Nullable File file) {
Set<CacheOperation> operations;
synchronized (synchronizationMutex) {
operations = new HashSet<>(newOperations);
}
for (CacheOperation operation : operations) {
operation.onResourceDownloaded(url, file);
}
}
});
scheduledCount++;
}
if (scheduledCount == 0) {
completion.onCacheCompleted(urlsToCache);
}
}
}
public void clear(@NonNull Set<URL> urlsToClear) {
synchronized (synchronizationMutex) {
for (URL url : urlsToClear) {
String cacheKey = getCacheKey(url);
diskCache.remove(cacheKey);
if (cachedKeys != null) {
cachedKeys.remove(cacheKey);
}
}
}
}
@Nullable
@Override
public File getCachedFile(@NonNull URL contentURL) {
File file = diskCache.fetch(getCacheKey(contentURL));
if (!file.exists()) {
return null;
}
return file;
}
private boolean hasCachedData(@NonNull String cacheKey) {
synchronized (synchronizationMutex) {
if (cachedKeys == null) {
cachedKeys = diskCache.getCacheKeys();
}
return cachedKeys.contains(cacheKey);
}
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/cache/ContentDownloader.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.asset.cache;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class ContentDownloader implements Runnable {
public interface DownloadCallback {
void onResourceDownloaded(@NonNull URL url, @Nullable File file);
}
private interface DownloadOperation extends Runnable {
}
private static final String LOG_TAG = ContentDownloader.class.getCanonicalName();
private final @NonNull BlockingQueue<DownloadOperation> operations;
public ContentDownloader() {
operations = new LinkedBlockingQueue<>();
}
@Override
public void run() {
while (true) {
try {
DownloadOperation operation = operations.take();
operation.run();
} catch (InterruptedException ex) {
// This means our parent thread requested us killed, RIP.
return;
}
}
}
public void downloadAsync(final @NonNull URL url, final @NonNull File targetFile, final @NonNull DownloadCallback callback) {
operations.offer(new DownloadOperation() {
private File download(@NonNull URL url) {
HttpURLConnection connection = null;
InputStream inputStream = null;
OutputStream outputStream = null;
try {
connection = (HttpURLConnection) url.openConnection();
if (connection.getResponseCode() != 200) {
return null;
}
inputStream = connection.getInputStream();
outputStream = new FileOutputStream(targetFile);
byte[] buffer = new byte[4096];
while (true) {
int read = inputStream.read(buffer);
if (read == -1) {
break;
}
outputStream.write(buffer, 0, read);
}
return targetFile;
} catch (Exception ex) {
Log.e(LOG_TAG, "Failed to download content for url " + url, ex);
return null;
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
} catch (IOException ex) {
Log.e(LOG_TAG, "Failed to close streams", ex);
}
if (connection != null) {
connection.disconnect();
}
}
}
@Override
public void run() {
callback.onResourceDownloaded(url, download(url));
}
});
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/cache/DiskCache.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.asset.cache;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Log;
import java.io.File;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
class DiskCache {
private static final String LOG_TAG = DiskCache.class.getCanonicalName();
private final @NonNull File cacheDirectory;
public DiskCache(@NonNull Context context) {
cacheDirectory = context.getCacheDir();
}
public void remove(final @NonNull String key) {
File diskFile = new File(cacheDirectory, key);
if (!diskFile.delete()) {
Log.w(LOG_TAG, "Failed to delete cache file \"" + diskFile.getAbsolutePath() + "\"");
}
}
@NonNull
public File fetch(@NonNull String key) {
return new File(cacheDirectory, key);
}
@NonNull
public Set<String> getCacheKeys() {
String[] files = cacheDirectory.list();
Set<String> keys = new HashSet<>(files.length);
Collections.addAll(keys, files);
return keys;
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/handlers/BitmapAssetHandler.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.asset.handlers;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Parcel;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import com.facebook.notifications.internal.asset.Asset;
import com.facebook.notifications.internal.asset.AssetManager;
import com.facebook.notifications.internal.utilities.InvalidParcelException;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
/**
* Handles assets of the bitmap type
*/
public class BitmapAssetHandler implements AssetManager.AssetHandler<BitmapAssetHandler.BitmapAsset> {
/**
* A resource implementation for Bitmaps read from disk
*/
static class BitmapAsset implements Asset {
public static final Creator<BitmapAsset> CREATOR = new Creator<BitmapAsset>() {
@Override
public BitmapAsset createFromParcel(Parcel source) {
return new BitmapAsset(source);
}
@Override
public BitmapAsset[] newArray(int size) {
return new BitmapAsset[size];
}
};
private final @NonNull File createdFrom;
private transient @Nullable Bitmap bitmap;
private BitmapAsset(@NonNull File createdFrom) {
this.createdFrom = createdFrom;
}
private BitmapAsset(@NonNull Parcel parcel) {
createdFrom = new File(parcel.readString());
}
@Nullable
private static Bitmap decodeBitmap(@NonNull File file) {
try {
// NOTE: We must be careful when decoding images on android. If a malicious push sends down
// a payload image that is too large for us to reasonably decode, we must ensure that we can
// safely fall back to a lower resolution if we don't have the memory for it.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
while (true) {
InputStream cachedData = new FileInputStream(file);
try {
return BitmapFactory.decodeStream(cachedData, null, options);
} catch (OutOfMemoryError ex) {
// Ignore out of memory, try loading with less requested resolution.
System.gc();
options.inSampleSize *= 2;
} finally {
cachedData.close();
}
}
} catch (IOException ex) {
Log.e(LOG_TAG, "IO Exception!", ex);
return null;
}
}
@NonNull
public File getCreatedFrom() {
return createdFrom;
}
@NonNull
public Bitmap getBitmap() {
if (bitmap == null) {
bitmap = decodeBitmap(createdFrom);
if (bitmap == null) {
throw new RuntimeException("Failed to decode bitmap from file");
}
}
return bitmap;
}
@NonNull
@Override
public String getType() {
return TYPE;
}
@Override
public void validate() throws InvalidParcelException {
if (!createdFrom.exists()) {
throw new InvalidParcelException(
new FileNotFoundException(
"Bitmap cache file does not exist: " + createdFrom.getAbsolutePath()
)
);
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(createdFrom.getAbsolutePath());
}
}
public static final String TYPE = "Image";
private static final String LOG_TAG = BitmapAssetHandler.class.getCanonicalName();
@Nullable
@Override
public Set<URL> getCacheURLs(@NonNull JSONObject payload) {
try {
URL url = new URL(payload.getString("url"));
Set<URL> set = new HashSet<>();
set.add(url);
return set;
} catch (MalformedURLException ex) {
return null;
} catch (JSONException ex) {
return null;
}
}
@Nullable
@Override
public BitmapAsset createAsset(@NonNull JSONObject payload, @NonNull AssetManager.AssetCache cache) {
try {
URL url = new URL(payload.getString("url"));
File cacheFile = cache.getCachedFile(url);
if (cacheFile == null) {
return null;
}
return new BitmapAsset(cacheFile);
} catch (MalformedURLException ex) {
Log.e(LOG_TAG, "JSON key 'url' was not a valid URL", ex);
return null;
} catch (JSONException ex) {
Log.e(LOG_TAG, "JSON exception", ex);
return null;
}
}
@NonNull
@Override
public View createView(@NonNull BitmapAsset asset, @NonNull Context context) {
ImageView imageView = new ImageView(context);
imageView.setImageBitmap(asset.getBitmap());
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
imageView.setAdjustViewBounds(true);
return imageView;
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/handlers/ColorAssetHandler.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.asset.handlers;
import android.content.Context;
import android.os.Parcel;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import com.facebook.notifications.internal.asset.Asset;
import com.facebook.notifications.internal.asset.AssetManager;
import com.facebook.notifications.internal.utilities.InvalidParcelException;
import org.json.JSONException;
import org.json.JSONObject;
import java.net.URL;
import java.util.Set;
/**
* Handles assets of Color type.
*/
public class ColorAssetHandler implements AssetManager.AssetHandler<ColorAssetHandler.ColorAsset> {
/**
* An asset implementation for a single static color
*/
static class ColorAsset implements Asset {
public static final Creator<ColorAsset> CREATOR = new Creator<ColorAsset>() {
@Override
public ColorAsset createFromParcel(Parcel source) {
return new ColorAsset(source);
}
@Override
public ColorAsset[] newArray(int size) {
return new ColorAsset[size];
}
};
private final int color;
private ColorAsset(int color) {
this.color = color;
}
private ColorAsset(@NonNull Parcel parcel) {
color = parcel.readInt();
}
public int getColor() {
return color;
}
@NonNull
@Override
public String getType() {
return TYPE;
}
@Override
public void validate() throws InvalidParcelException {
// Nothing to validate
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(color);
}
}
@SuppressWarnings("ViewConstructor")
private static class ColorView extends FrameLayout {
public ColorView(Context context, int color) {
super(context);
setBackgroundColor(color);
}
}
public static final String TYPE = "Color";
private static final String LOG_TAG = ColorAssetHandler.class.getCanonicalName();
/**
* Parses a string in the format '#RRGGBBAA', and returns it as an android-compatible color
*
* @param input The string to parse
* @return The color, in an android-compatible format
*/
public static int fromRGBAHex(@Nullable String input) {
if (input == null || input.equals("")) {
return 0;
}
input = input.substring(1);
try {
long value = Long.parseLong(input, 16);
// Android color has alpha first, not last. Flip the bytes around.
return (int) ((value >> 8) | ((value & 0xFF) << 24));
} catch (NumberFormatException ex) {
return 0;
}
}
@Nullable
@Override
public Set<URL> getCacheURLs(@NonNull JSONObject payload) {
return null;
}
@Nullable
@Override
public ColorAsset createAsset(@NonNull JSONObject payload, @NonNull AssetManager.AssetCache cache) {
try {
return new ColorAsset(fromRGBAHex(payload.getString("rgbaHex")));
} catch (JSONException ex) {
Log.e(LOG_TAG, "JSON Exception", ex);
return null;
}
}
@NonNull
@Override
public View createView(@NonNull ColorAsset asset, @NonNull Context context) {
return new ColorView(context, asset.getColor());
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/handlers/GifAssetHandler.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.asset.handlers;
import android.content.Context;
import android.os.Parcel;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import com.facebook.notifications.internal.asset.Asset;
import com.facebook.notifications.internal.asset.AssetManager;
import com.facebook.notifications.internal.utilities.GifDecoder;
import com.facebook.notifications.internal.utilities.InvalidParcelException;
import com.facebook.notifications.internal.view.GifView;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
/**
* Handles assets of the GIF type.
*/
public class GifAssetHandler implements AssetManager.AssetHandler<GifAssetHandler.GifAsset> {
/**
* A resource implementation for GIFs read from disk
*/
static class GifAsset implements Asset {
public static final Creator<GifAsset> CREATOR = new Creator<GifAsset>() {
@Override
public GifAsset createFromParcel(Parcel source) {
return new GifAsset(source);
}
@Override
public GifAsset[] newArray(int size) {
return new GifAsset[size];
}
};
private final @NonNull File createdFrom;
private transient @Nullable GifDecoder decoder;
private GifAsset(@NonNull File createdFrom) {
this.createdFrom = createdFrom;
}
private GifAsset(@NonNull Parcel source) {
createdFrom = new File(source.readString());
}
@Nullable
private static GifDecoder decodeGif(@NonNull File file) {
long fileLength = file.length();
if (fileLength == 0 || fileLength > Integer.MAX_VALUE) {
return null;
}
byte[] bytes = new byte[(int) fileLength];
try {
FileInputStream fileInputStream = new FileInputStream(file);
int fileOffset = 0;
try {
while (true) {
int remaining = (bytes.length - fileOffset);
if (remaining == 0) {
break;
}
int read = fileInputStream.read(bytes, fileOffset, remaining);
if (read == -1) {
throw new IOException("File was shorter than expected!");
}
fileOffset += read;
}
} finally {
fileInputStream.close();
}
} catch (IOException ex) {
Log.e(LOG_TAG, "IO Exception while reading GIF data", ex);
return null;
}
GifDecoder decoder = new GifDecoder();
decoder.read(bytes);
return decoder;
}
@NonNull
public File getCreatedFrom() {
return createdFrom;
}
@NonNull
public GifDecoder getDecoder() {
if (decoder == null) {
decoder = decodeGif(createdFrom);
if (decoder == null) {
throw new RuntimeException("Failed to decode GIF");
}
}
return decoder;
}
@NonNull
@Override
public String getType() {
return TYPE;
}
@Override
public void validate() throws InvalidParcelException {
if (!createdFrom.exists()) {
throw new InvalidParcelException(
new FileNotFoundException(
"GIF cache file does not exist: " + createdFrom.getAbsolutePath()
)
);
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(createdFrom.getAbsolutePath());
}
}
public static final String TYPE = "GIF";
private static final String LOG_TAG = GifAssetHandler.class.getCanonicalName();
@Nullable
@Override
public Set<URL> getCacheURLs(@NonNull JSONObject payload) {
try {
URL url = new URL(payload.getString("url"));
Set<URL> set = new HashSet<>();
set.add(url);
return set;
} catch (MalformedURLException ex) {
return null;
} catch (JSONException ex) {
return null;
}
}
@Nullable
@Override
public GifAsset createAsset(@NonNull JSONObject payload, @NonNull AssetManager.AssetCache cache) {
try {
URL url = new URL(payload.getString("url"));
File cacheFile = cache.getCachedFile(url);
if (cacheFile == null) {
return null;
}
return new GifAsset(cacheFile);
} catch (MalformedURLException ex) {
Log.e(LOG_TAG, "JSON key 'url' was not a valid URL.", ex);
return null;
} catch (JSONException ex) {
Log.e(LOG_TAG, "JSON exception", ex);
return null;
}
}
@NonNull
@Override
public View createView(@NonNull GifAsset asset, @NonNull Context context) {
return new GifView(context, asset.getDecoder());
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/ActionConfiguration.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.configuration;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.facebook.notifications.internal.content.Content;
import com.facebook.notifications.internal.content.TextContent;
import org.json.JSONException;
import org.json.JSONObject;
import static com.facebook.notifications.internal.asset.handlers.ColorAssetHandler.fromRGBAHex;
public class ActionConfiguration implements Parcelable {
public static final Creator<ActionConfiguration> CREATOR = new Creator<ActionConfiguration>() {
@Override
public ActionConfiguration createFromParcel(Parcel source) {
return new ActionConfiguration(source);
}
@Override
public ActionConfiguration[] newArray(int size) {
return new ActionConfiguration[size];
}
};
private final int backgroundColor;
private final int borderColor;
private final float borderWidth;
private final @Nullable Content content;
private final @Nullable Uri actionUri;
public ActionConfiguration(JSONObject json) throws JSONException {
backgroundColor = fromRGBAHex(json.optString("backgroundColor"));
borderColor = fromRGBAHex(json.optString("borderColor"));
borderWidth = (float) json.optDouble("borderWidth", 0);
JSONObject contentJSON = json.optJSONObject("content");
content = contentJSON == null ? null : new TextContent(contentJSON);
String jsonUri = json.optString("url", null);
actionUri = jsonUri == null ? null : Uri.parse(jsonUri);
}
private ActionConfiguration(Parcel source) {
ClassLoader classLoader = getClass().getClassLoader();
backgroundColor = source.readInt();
borderColor = source.readInt();
borderWidth = source.readFloat();
content = source.readParcelable(classLoader);
actionUri = source.readParcelable(classLoader);
}
public int getBackgroundColor() {
return backgroundColor;
}
public int getBorderColor() {
return borderColor;
}
public float getBorderWidth() {
return borderWidth;
}
@Nullable
public Content getContent() {
return content;
}
@Nullable
public Uri getActionUri() {
return actionUri;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(backgroundColor);
dest.writeInt(borderColor);
dest.writeFloat(borderWidth);
dest.writeParcelable(content, flags);
dest.writeParcelable(actionUri, flags);
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/ActionsConfiguration.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.configuration;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.facebook.notifications.internal.asset.Asset;
import com.facebook.notifications.internal.asset.AssetManager;
import com.facebook.notifications.internal.utilities.EnumCreator;
import com.facebook.notifications.internal.utilities.InvalidParcelException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class ActionsConfiguration implements Parcelable {
public enum ActionsStyle implements Parcelable {
Attached,
Detached;
public static final Creator<ActionsStyle> CREATOR = new EnumCreator<>(ActionsStyle.class, values());
public static ActionsStyle parse(String input) {
switch (input) {
case "attached":
return ActionsStyle.Attached;
case "detached":
return ActionsStyle.Detached;
default:
return ActionsStyle.Attached;
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(ordinal());
}
}
public enum ActionsLayoutStyle implements Parcelable {
Vertical,
Horizontal;
public static final Creator<ActionsLayoutStyle> CREATOR = new EnumCreator<>(ActionsLayoutStyle.class, values());
public static ActionsLayoutStyle parse(String input) {
switch (input) {
case "vertical":
return ActionsLayoutStyle.Vertical;
case "horizontal":
return ActionsLayoutStyle.Horizontal;
default:
return ActionsLayoutStyle.Vertical;
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(ordinal());
}
}
public static final Creator<ActionsConfiguration> CREATOR = new Creator<ActionsConfiguration>() {
@Override
public ActionsConfiguration createFromParcel(Parcel source) {
return new ActionsConfiguration(source);
}
@Override
public ActionsConfiguration[] newArray(int size) {
return new ActionsConfiguration[size];
}
};
private final ActionsStyle style;
private final ActionsLayoutStyle layoutStyle;
private final Asset background;
private final float topInset;
private final float contentInset;
private final float cornerRadius;
private final @NonNull ActionConfiguration[] actions;
private final float height;
private ActionsConfiguration(@NonNull JSONObject json, @NonNull AssetManager assetManager) throws JSONException {
style = ActionsStyle.parse(json.optString("style"));
layoutStyle = ActionsLayoutStyle.parse(json.optString("layoutStyle"));
background = assetManager.inflateAsset(json.optJSONObject("background"));
topInset = (float) json.optDouble("topInset", 0);
contentInset = (float) json.optDouble("contentInset", 0);
cornerRadius = (float) json.optDouble("cornerRadius", 0);
JSONArray rawActions = json.getJSONArray("actions");
actions = new ActionConfiguration[rawActions.length()];
for (int jsonIndex = 0; jsonIndex < actions.length; jsonIndex++) {
actions[jsonIndex] = new ActionConfiguration(rawActions.getJSONObject(jsonIndex));
}
height = (float) json.optDouble("height", 44);
}
private ActionsConfiguration(Parcel source) {
ClassLoader loader = getClass().getClassLoader();
style = source.readParcelable(loader);
layoutStyle = source.readParcelable(loader);
background = source.readParcelable(loader);
topInset = source.readFloat();
contentInset = source.readFloat();
cornerRadius = source.readFloat();
actions = source.createTypedArray(ActionConfiguration.CREATOR);
height = source.readFloat();
}
@Nullable
public static ActionsConfiguration fromJSON(@Nullable JSONObject json, @NonNull AssetManager assetManager) throws JSONException {
if (json == null) {
return null;
}
return new ActionsConfiguration(json, assetManager);
}
public void validate() throws InvalidParcelException {
if (background != null) {
background.validate();
}
}
public ActionsStyle getStyle() {
return style;
}
public ActionsLayoutStyle getLayoutStyle() {
return layoutStyle;
}
public Asset getBackground() {
return background;
}
public float getTopInset() {
return topInset;
}
public float getContentInset() {
return contentInset;
}
public float getCornerRadius() {
return cornerRadius;
}
@NonNull
public ActionConfiguration[] getActions() {
return actions;
}
public float getHeight() {
return height;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(style, flags);
dest.writeParcelable(layoutStyle, flags);
dest.writeParcelable(background, flags);
dest.writeFloat(topInset);
dest.writeFloat(contentInset);
dest.writeFloat(cornerRadius);
dest.writeTypedArray(actions, flags);
dest.writeFloat(height);
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/BodyConfiguration.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.configuration;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.facebook.notifications.internal.asset.Asset;
import com.facebook.notifications.internal.asset.AssetManager;
import com.facebook.notifications.internal.content.Content;
import com.facebook.notifications.internal.content.ContentManager;
import com.facebook.notifications.internal.content.TextContent;
import com.facebook.notifications.internal.utilities.InvalidParcelException;
import org.json.JSONException;
import org.json.JSONObject;
public class BodyConfiguration implements Parcelable {
public static final Creator<BodyConfiguration> CREATOR = new Creator<BodyConfiguration>() {
@Override
public BodyConfiguration createFromParcel(Parcel source) {
return new BodyConfiguration(source);
}
@Override
public BodyConfiguration[] newArray(int size) {
return new BodyConfiguration[size];
}
};
private final @Nullable Asset background;
private final @Nullable Content content;
private BodyConfiguration(
@NonNull JSONObject json,
@NonNull AssetManager assetManager,
@NonNull ContentManager contentManager
) throws JSONException {
background = assetManager.inflateAsset(json.getJSONObject("background"));
// TODO: Go through content manager
JSONObject contentJSON = json.optJSONObject("content");
content = contentJSON != null
? new TextContent(contentJSON)
: null;
}
private BodyConfiguration(Parcel source) {
ClassLoader loader = getClass().getClassLoader();
background = source.readParcelable(loader);
content = source.readParcelable(loader);
}
@Nullable
public static BodyConfiguration fromJSON(@Nullable JSONObject json, @NonNull AssetManager assetManager, @NonNull ContentManager contentManager) throws JSONException {
if (json == null) {
return null;
}
return new BodyConfiguration(json, assetManager, contentManager);
}
public void validate() throws InvalidParcelException {
if (background != null) {
background.validate();
}
}
@Nullable
public Asset getBackground() {
return background;
}
@Nullable
public Content getContent() {
return content;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(background, flags);
dest.writeParcelable(content, flags);
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/CardConfiguration.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.configuration;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.facebook.notifications.internal.asset.AssetManager;
import com.facebook.notifications.internal.asset.handlers.ColorAssetHandler;
import com.facebook.notifications.internal.content.ContentManager;
import com.facebook.notifications.internal.utilities.EnumCreator;
import com.facebook.notifications.internal.utilities.InvalidParcelException;
import org.json.JSONException;
import org.json.JSONObject;
public class CardConfiguration implements Parcelable {
public enum CardSize implements Parcelable {
Invalid,
Small,
Medium,
Large;
public static final Creator<CardSize> CREATOR = new EnumCreator<>(CardSize.class, values());
public static CardSize parse(String input) {
switch (input) {
case "small":
return CardSize.Small;
case "medium":
return CardSize.Medium;
case "large":
return CardSize.Large;
default:
return CardSize.Invalid;
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(ordinal());
}
}
private static final String LOG_TAG = CardConfiguration.class.getCanonicalName();
private final CardSize cardSize;
private final float cornerRadius;
private final float contentInset;
private final int backdropColor;
private final @Nullable HeroConfiguration heroConfiguration;
private final @Nullable BodyConfiguration bodyConfiguration;
private final @Nullable ActionsConfiguration actionsConfiguration;
public static final Creator<CardConfiguration> CREATOR = new Creator<CardConfiguration>() {
@Override
public CardConfiguration createFromParcel(Parcel source) {
try {
CardConfiguration configuration = new CardConfiguration(source);
configuration.validate();
return configuration;
} catch (InvalidParcelException ex) {
Log.w(LOG_TAG, "Failed to decode card configuration", ex);
return null;
}
}
@Override
public CardConfiguration[] newArray(int size) {
return new CardConfiguration[size];
}
};
public CardConfiguration(@NonNull JSONObject jsonObject, @NonNull AssetManager assetManager, @NonNull ContentManager contentManager) throws JSONException {
cardSize = CardSize.parse(jsonObject.getString("size"));
cornerRadius = (float) jsonObject.optDouble("cornerRadius", 0.0);
contentInset = (float) jsonObject.optDouble("contentInset", 10.0);
backdropColor = ColorAssetHandler.fromRGBAHex(jsonObject.getString("backdropColor"));
heroConfiguration = HeroConfiguration.fromJSON(jsonObject.optJSONObject("hero"), assetManager, contentManager);
bodyConfiguration = BodyConfiguration.fromJSON(jsonObject.optJSONObject("body"), assetManager, contentManager);
actionsConfiguration = ActionsConfiguration.fromJSON(jsonObject.optJSONObject("actions"), assetManager);
}
private CardConfiguration(@NonNull Parcel source) {
ClassLoader loader = getClass().getClassLoader();
cardSize = source.readParcelable(loader);
cornerRadius = source.readFloat();
contentInset = source.readFloat();
backdropColor = source.readInt();
heroConfiguration = source.readParcelable(loader);
bodyConfiguration = source.readParcelable(loader);
actionsConfiguration = source.readParcelable(loader);
}
public void validate() throws InvalidParcelException {
if (heroConfiguration != null) {
heroConfiguration.validate();
}
if (bodyConfiguration != null) {
bodyConfiguration.validate();
}
if (actionsConfiguration != null) {
actionsConfiguration.validate();
}
}
public CardSize getCardSize() {
return cardSize;
}
public float getCornerRadius() {
return cornerRadius;
}
public float getContentInset() {
return contentInset;
}
public int getBackdropColor() {
return backdropColor;
}
@Nullable
public HeroConfiguration getHeroConfiguration() {
return heroConfiguration;
}
@Nullable
public BodyConfiguration getBodyConfiguration() {
return bodyConfiguration;
}
@Nullable
public ActionsConfiguration getActionsConfiguration() {
return actionsConfiguration;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(cardSize, flags);
dest.writeFloat(cornerRadius);
dest.writeFloat(contentInset);
dest.writeInt(backdropColor);
dest.writeParcelable(heroConfiguration, flags);
dest.writeParcelable(bodyConfiguration, flags);
dest.writeParcelable(actionsConfiguration, flags);
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/HeroConfiguration.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.configuration;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.facebook.notifications.internal.asset.Asset;
import com.facebook.notifications.internal.asset.AssetManager;
import com.facebook.notifications.internal.content.Content;
import com.facebook.notifications.internal.content.ContentManager;
import com.facebook.notifications.internal.content.TextContent;
import com.facebook.notifications.internal.utilities.InvalidParcelException;
import org.json.JSONException;
import org.json.JSONObject;
public class HeroConfiguration implements Parcelable {
public static final Creator<HeroConfiguration> CREATOR = new Creator<HeroConfiguration>() {
@Override
public HeroConfiguration createFromParcel(Parcel source) {
return new HeroConfiguration(source);
}
@Override
public HeroConfiguration[] newArray(int size) {
return new HeroConfiguration[size];
}
};
private final float height;
private final @Nullable Asset background;
private final @Nullable Content content;
private final Content.VerticalAlignment contentVerticalAlignment;
private HeroConfiguration(
@NonNull JSONObject json,
@NonNull AssetManager assetManager,
@NonNull ContentManager contentManager
) throws JSONException {
height = (float) json.optDouble("height", -1);
background = assetManager.inflateAsset(json.getJSONObject("background"));
// TODO: Go through content manager
JSONObject contentJSON = json.optJSONObject("content");
content = contentJSON != null
? new TextContent(contentJSON)
: null;
contentVerticalAlignment = Content.VerticalAlignment.parse(json.optString("contentAlign"));
}
private HeroConfiguration(Parcel parcel) {
ClassLoader loader = getClass().getClassLoader();
height = parcel.readFloat();
background = parcel.readParcelable(loader);
content = parcel.readParcelable(loader);
contentVerticalAlignment = parcel.readParcelable(loader);
}
/**
* Create a hero configuration from a possibly `null` JSON payload
*
* @param json JSON Payload. Can be null
* @param assetManager Asset manager to use for any assets in the hero
* @param contentManager Content manager to use for any content in the hero
* @return A hero configuration if the JSON payload exists, or `null` if it does not
* @throws JSONException if the JSON is in an invalid format
*/
@Nullable
public static HeroConfiguration fromJSON(
@Nullable JSONObject json,
@NonNull AssetManager assetManager,
@NonNull ContentManager contentManager
) throws JSONException {
if (json == null) {
return null;
}
return new HeroConfiguration(json, assetManager, contentManager);
}
public void validate() throws InvalidParcelException {
if (background != null) {
background.validate();
}
}
public float getHeight() {
return height;
}
@Nullable
public Asset getBackground() {
return background;
}
@Nullable
public Content getContent() {
return content;
}
public Content.VerticalAlignment getContentVerticalAlignment() {
return contentVerticalAlignment;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeFloat(height);
dest.writeParcelable(background, flags);
dest.writeParcelable(content, flags);
dest.writeParcelable(contentVerticalAlignment, flags);
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/content/Content.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.content;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.view.View;
import com.facebook.notifications.internal.utilities.EnumCreator;
public interface Content extends Parcelable {
enum VerticalAlignment implements Parcelable {
Top,
Center,
Bottom;
public static final Creator<VerticalAlignment> CREATOR = new EnumCreator<>(VerticalAlignment.class, values());
public static VerticalAlignment parse(String input) {
switch (input) {
case "top":
return VerticalAlignment.Top;
case "center":
return VerticalAlignment.Center;
case "bottom":
return VerticalAlignment.Bottom;
default:
return VerticalAlignment.Center;
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(ordinal());
}
}
/**
* Attempt to apply this content to a given view
* @param view The view to apply to
*/
void applyTo(@NonNull View view);
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/content/ContentManager.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.content;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import com.facebook.notifications.internal.utilities.InvalidParcelException;
/**
* Manager for content
*/
public class ContentManager implements Parcelable {
public static final Creator<ContentManager> CREATOR = new Creator<ContentManager>() {
@Override
public ContentManager createFromParcel(Parcel source) {
return new ContentManager(source);
}
@Override
public ContentManager[] newArray(int size) {
return new ContentManager[size];
}
};
// TODO: Extensible like AssetManager, but for content
public ContentManager() {
}
public ContentManager(ContentManager other) {
}
public ContentManager(@NonNull Parcel source) {
}
public void setContext(@NonNull Context context) {
}
public void validate() throws InvalidParcelException {
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/content/TextContent.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.content;
import android.graphics.Typeface;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.Gravity;
import android.view.View;
import android.widget.TextView;
import com.facebook.notifications.internal.asset.handlers.ColorAssetHandler;
import com.facebook.notifications.internal.utilities.EnumCreator;
import com.facebook.notifications.internal.utilities.FontUtilities;
import org.json.JSONException;
import org.json.JSONObject;
public class TextContent implements Content {
public enum Alignment implements Parcelable {
Left,
Right,
Center;
public static final Creator<Alignment> CREATOR = new EnumCreator<>(Alignment.class, values());
public static Alignment parse(@NonNull String input) {
switch (input) {
case "left":
return Left;
case "right":
return Right;
case "center":
return Center;
default:
return Left;
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(ordinal());
}
}
public static final Creator<TextContent> CREATOR = new Creator<TextContent>() {
@Override
public TextContent createFromParcel(Parcel source) {
return new TextContent(source);
}
@Override
public TextContent[] newArray(int size) {
return new TextContent[size];
}
};
private final @NonNull String text;
private final int textColor;
private final @Nullable String typeface;
private final float typefaceSize;
private final Alignment textAlignment;
public TextContent(@NonNull JSONObject json) throws JSONException {
text = json.optString("text", "");
textColor = ColorAssetHandler.fromRGBAHex(json.optString("color"));
typeface = json.optString("font");
typefaceSize = (float) json.optDouble("size", 18); // Default to 18sp, or 'medium' size.
textAlignment = Alignment.parse(json.optString("align", "center"));
}
private TextContent(Parcel source) {
text = source.readString();
textColor = source.readInt();
typeface = source.readString();
typefaceSize = source.readFloat();
textAlignment = source.readParcelable(getClass().getClassLoader());
}
@Override
public void applyTo(@NonNull View view) {
if (view instanceof TextView) {
TextView textView = (TextView)view;
textView.setText(getText());
textView.setTextColor(getTextColor());
Typeface typeface = FontUtilities.parseFont(getTypeface());
typeface = typeface != null ? typeface : Typeface.DEFAULT;
textView.setTypeface(typeface);
textView.setTextSize(getTypefaceSize());
switch (getTextAlignment()) {
case Left:
textView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
break;
case Center:
textView.setGravity(Gravity.CENTER);
break;
case Right:
textView.setGravity(Gravity.RIGHT | Gravity.CENTER_VERTICAL);
break;
}
}
}
@NonNull
public String getText() {
return text;
}
public int getTextColor() {
return textColor;
}
@Nullable
public String getTypeface() {
return typeface;
}
public float getTypefaceSize() {
return typefaceSize;
}
public Alignment getTextAlignment() {
return textAlignment;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(text);
dest.writeInt(textColor);
dest.writeString(typeface);
dest.writeFloat(typefaceSize);
dest.writeParcelable(textAlignment, flags);
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/EnumCreator.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.utilities;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.reflect.Array;
public class EnumCreator<T extends Enum> implements Parcelable.Creator<T> {
private final Class<T> kls;
private final T[] values;
public EnumCreator(Class<T> kls, T[] values) {
this.kls = kls;
this.values = values;
}
@Override
public T createFromParcel(Parcel source) {
return values[source.readInt()];
}
@Override
@SuppressWarnings("unchecked")
public T[] newArray(int size) {
return (T[]) Array.newInstance(kls, size);
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/FontUtilities.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.utilities;
import android.graphics.Typeface;
import android.support.annotation.Nullable;
public class FontUtilities {
public static @Nullable Typeface parseFont(@Nullable String fontName) {
if (fontName == null) {
return Typeface.DEFAULT;
}
switch (fontName.toLowerCase()) {
case "system-regular": return Typeface.DEFAULT;
case "system-light": return Typeface.create("sans-serif-light", Typeface.NORMAL);
case "system-bold": return Typeface.DEFAULT_BOLD;
case "system-italic": return Typeface.defaultFromStyle(Typeface.ITALIC);
case "system-bolditalic": return Typeface.defaultFromStyle(Typeface.BOLD_ITALIC);
default: return Typeface.create(fontName, Typeface.NORMAL);
}
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/GifDecoder.java
================================================
/**
* Copyright (c) 2013 Xcellent Creations, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.facebook.notifications.internal.utilities;
import android.graphics.Bitmap;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
/**
* Reads frame data from a GIF image source and decodes it into individual frames
* for animation purposes. Image data can be read from either and InputStream source
* or a byte[].
*
* This class is optimized for running animations with the frames, there
* are no methods to get individual frame images, only to decode the next frame in the
* animation sequence. Instead, it lowers its memory footprint by only housing the minimum
* data necessary to decode the next frame in the animation sequence.
*
* The animation must be manually moved forward using {@link #advance()} before requesting the next
* frame. This method must also be called before you request the first frame or an error will
* occur.
*
* Implementation adapted from sample code published in Lyons. (2004). <em>Java for Programmers</em>,
* republished under the MIT Open Source License
*/
@SuppressWarnings("ALL")
public class GifDecoder {
private static final String TAG = GifDecoder.class.getSimpleName();
/**
* File read status: No errors.
*/
public static final int STATUS_OK = 0;
/**
* File read status: Error decoding file (may be partially decoded)
*/
public static final int STATUS_FORMAT_ERROR = 1;
/**
* File read status: Unable to open source.
*/
public static final int STATUS_OPEN_ERROR = 2;
/**
* max decoder pixel stack size
*/
protected static final int MAX_STACK_SIZE = 4096;
/**
* GIF Disposal Method meaning take no action
*/
private static final int DISPOSAL_UNSPECIFIED = 0;
/**
* GIF Disposal Method meaning leave canvas from previous frame
*/
private static final int DISPOSAL_NONE = 1;
/**
* GIF Disposal Method meaning clear canvas to background color
*/
private static final int DISPOSAL_BACKGROUND = 2;
/**
* GIF Disposal Method meaning clear canvas to frame before last
*/
private static final int DISPOSAL_PREVIOUS = 3;
/**
* Global status code of GIF data parsing
*/
protected int status;
//Global File Header values and parsing flags
protected int width; // full image width
protected int height; // full image height
protected boolean gctFlag; // global color table used
protected int gctSize; // size of global color table
protected int loopCount = 1; // iterations; 0 = repeat forever
protected int[] gct; // global color table
protected int[] act; // active color table
protected int bgIndex; // background color index
protected int bgColor; // background color
protected int pixelAspect; // pixel aspect ratio
protected boolean lctFlag; // local color table flag
protected int lctSize; // local color table size
// Raw GIF data from input source
protected ByteBuffer rawData;
// Raw data read working array
protected byte[] block = new byte[256]; // current data block
protected int blockSize = 0; // block size last graphic control extension info
// LZW decoder working arrays
protected short[] prefix;
protected byte[] suffix;
protected byte[] pixelStack;
protected byte[] mainPixels;
protected int[] mainScratch, copyScratch;
protected ArrayList<GifFrame> frames; // frames read from current file
protected GifFrame currentFrame;
protected Bitmap previousImage, currentImage, renderImage;
protected int framePointer;
protected int frameCount;
/**
* Inner model class housing metadata for each frame
*/
private static class GifFrame {
public int ix, iy, iw, ih;
/* Control Flags */
public boolean interlace;
public boolean transparency;
/* Disposal Method */
public int dispose;
/* Transparency Index */
public int transIndex;
/* Delay, in ms, to next frame */
public int delay;
/* Index in the raw buffer where we need to start reading to decode */
public int bufferFrameStart;
/* Local Color Table */
public int[] lct;
}
/**
* Move the animation frame counter forward
*/
public void advance() {
framePointer = (framePointer + 1) % frameCount;
}
/**
* Recycle any bitmaps held by this GIF decoder, as well as any intermediate data. After
* recycling, the decoder can no longer be used.
*/
public void recycle() {
if (previousImage != null) {
previousImage.recycle();
}
if (currentImage != null) {
currentImage.recycle();
}
if (renderImage != null) {
renderImage.recycle();
}
gct = null;
act = null;
block = null;
prefix = null;
suffix = null;
pixelStack = null;
mainPixels = null;
mainScratch = null;
copyScratch = null;
currentFrame = null;
frames = null;
}
/**
* Gets display duration for specified frame.
*
* @param n int index of frame
* @return delay in milliseconds
*/
public int getDelay(int n) {
int delay = -1;
if ((n >= 0) && (n < frameCount)) {
delay = frames.get(n).delay;
}
return delay;
}
/**
* Gets display duration for the upcoming frame
*/
public int getNextDelay() {
if (frameCount <=0 || framePointer < 0) {
return -1;
}
return getDelay(framePointer);
}
/**
* Gets the number of frames read from file.
*
* @return frame count
*/
public int getFrameCount() {
return frameCount;
}
/**
* Gets the current index of the animation frame, or -1 if animation hasn't not yet started
*
* @return frame index
*/
public int getCurrentFrameIndex() {
return framePointer;
}
/**
* Gets the "Netscape" iteration count, if any. A count of 0 means repeat indefinitiely.
*
* @return iteration count if one was specified, else 1.
*/
public int getLoopCount() {
return loopCount;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
/**
* Get the next frame in the animation sequence.
*
* @return Bitmap representation of frame
*/
public Bitmap getNextFrame() {
if (frameCount <= 0 || framePointer < 0 || currentImage == null) {
return null;
}
GifFrame frame = frames.get(framePointer);
//Set the appropriate color table
if (frame.lct == null) {
act = gct;
} else {
act = frame.lct;
if (bgIndex == frame.transIndex) {
bgColor = 0;
}
}
int save = 0;
if (frame.transparency) {
save = act[frame.transIndex];
act[frame.transIndex] = 0; // set transparent color if specified
}
if (act == null) {
Log.w(TAG, "No Valid Color Table");
status = STATUS_FORMAT_ERROR; // no color table defined
return null;
}
setPixels(framePointer); // transfer pixel data to image
// Reset the transparent pixel in the color table
if (frame.transparency) {
act[frame.transIndex] = save;
}
return currentImage;
}
/**
* Reads GIF image from stream
*
* @param is containing GIF file.
* @return read status code (0 = no errors)
*/
public int read(InputStream is, int contentLength) {
long startTime = System.currentTimeMillis();
if (is != null) {
try {
int capacity = (contentLength > 0) ? (contentLength + 4096) : 4096;
ByteArrayOutputStream buffer = new ByteArrayOutputStream(capacity);
int nRead;
byte[] data = new byte[16384];
while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
read(buffer.toByteArray());
} catch (IOException e) {
Log.w(TAG, "Error reading data from stream", e);
}
} else {
status = STATUS_OPEN_ERROR;
}
try {
is.close();
} catch (Exception e) {
Log.w(TAG, "Error closing stream", e);
}
return status;
}
/**
* Reads GIF image from byte array
*
* @param data containing GIF file.
* @return read status code (0 = no errors)
*/
public int read(byte[] data) {
init();
if (data != null) {
//Initiliaze the raw data buffer
rawData = ByteBuffer.wrap(data);
rawData.rewind();
rawData.order(ByteOrder.LITTLE_ENDIAN);
readHeader();
if (!err()) {
readContents();
if (frameCount < 0) {
status = STATUS_FORMAT_ERROR;
}
}
} else {
status = STATUS_OPEN_ERROR;
}
return status;
}
/**
* Creates new frame image from current data (and previous frames as specified by their disposition codes).
*/
protected void setPixels(int frameIndex) {
GifFrame currentFrame = frames.get(frameIndex);
GifFrame previousFrame = null;
int previousIndex = frameIndex - 1;
if (previousIndex >= 0) {
previousFrame = frames.get(previousIndex);
}
// final location of blended pixels
final int[] dest = mainScratch;
// fill in starting image contents based on last image's dispose code
if (previousFrame != null && previousFrame.dispose > DISPOSAL_UNSPECIFIED) {
if (previousFrame.dispose == DISPOSAL_NONE && currentImage != null) {
// Start with the current image
currentImage.getPixels(dest, 0, width, 0, 0, width, height);
}
if (previousFrame.dispose == DISPOSAL_BACKGROUND) {
// Start with a canvas filled with the background color
int c = 0;
if (!currentFrame.transparency) {
c = bgColor;
}
for (int i = 0; i < previousFrame.ih; i++) {
int n1 = (previousFrame.iy + i) * width + previousFrame.ix;
int n2 = n1 + previousFrame.iw;
for (int k = n1; k < n2; k++) {
dest[k] = c;
}
}
}
if (previousFrame.dispose == DISPOSAL_PREVIOUS && previousImage != null) {
// Start with the previous frame
previousImage.getPixels(dest, 0, width, 0, 0, width, height);
}
}
//Decode pixels for this frame into the global pixels[] scratch
decodeBitmapData(currentFrame, mainPixels); // decode pixel data
// copy each source line to the appropriate place in the destination
int pass = 1;
int inc = 8;
int iline = 0;
for (int i = 0; i < currentFrame.ih; i++) {
int line = i;
if (currentFrame.interlace) {
if (iline >= currentFrame.ih) {
pass++;
switch (pass) {
case 2:
iline = 4;
break;
case 3:
iline = 2;
inc = 4;
break;
case 4:
iline = 1;
inc = 2;
break;
default:
break;
}
}
line = iline;
iline += inc;
}
line += currentFrame.iy;
if (line < height) {
int k = line * width;
int dx = k + currentFrame.ix; // start of line in dest
int dlim = dx + currentFrame.iw; // end of dest line
if ((k + width) < dlim) {
dlim = k + width; // past dest edge
}
int sx = i * currentFrame.iw; // start of line in source
while (dx < dlim) {
// map color and insert in destination
int index = ((int) mainPixels[sx++]) & 0xff;
int c = act[index];
if (c != 0) {
dest[dx] = c;
}
dx++;
}
}
}
//Copy pixels into previous image
currentImage.getPixels(copyScratch, 0, width, 0, 0, width, height);
previousImage.setPixels(copyScratch, 0, width, 0, 0, width, height);
//Set pixels for current image
currentImage.setPixels(dest, 0, width, 0, 0, width, height);
}
/**
* Decodes LZW image data into pixel array. Adapted from John Cristy's BitmapMagick.
*/
protected void decodeBitmapData(GifFrame frame, byte[] dstPixels) {
long startTime = System.currentTimeMillis();
long stepOne, stepTwo, stepThree;
if (frame != null) {
//Jump to the frame start position
rawData.position(frame.bufferFrameStart);
}
int nullCode = -1;
int npix = (frame == null) ? width * height : frame.iw * frame.ih;
int available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, count, i, datum, data_size, first, top, bi, pi;
if (dstPixels == null || dstPixels.length < npix) {
dstPixels = new byte[npix]; // allocate new pixel array
}
if (prefix == null) {
prefix = new short[MAX_STACK_SIZE];
}
if (suffix == null) {
suffix = new byte[MAX_STACK_SIZE];
}
if (pixelStack == null) {
pixelStack = new byte[MAX_STACK_SIZE + 1];
}
// Initialize GIF data stream decoder.
data_size = read();
clear = 1 << data_size;
end_of_information = clear + 1;
available = clear + 2;
old_code = nullCode;
code_size = data_size + 1;
code_mask = (1 << code_size) - 1;
for (code = 0; code < clear; code++) {
prefix[code] = 0; // XXX ArrayIndexOutOfBoundsException
suffix[code] = (byte) code;
}
// Decode GIF pixel stream.
datum = bits = count = first = top = pi = bi = 0;
for (i = 0; i < npix; ) {
if (top == 0) {
if (bits < code_size) {
// Load bytes until there are enough bits for a code.
if (count == 0) {
// Read a new data block.
count = readBlock();
if (count <= 0) {
break;
}
bi = 0;
}
datum += (((int) block[bi]) & 0xff) << bits;
bits += 8;
bi++;
count--;
continue;
}
// Get the next code.
code = datum & code_mask;
datum >>= code_size;
bits -= code_size;
// Interpret the code
if ((code > available) || (code == end_of_information)) {
break;
}
if (code == clear) {
// Reset decoder.
code_size = data_size + 1;
code_mask = (1 << code_size) - 1;
available = clear + 2;
old_code = nullCode;
continue;
}
if (old_code == nullCode) {
pixelStack[top++] = suffix[code];
old_code = code;
first = code;
continue;
}
in_code = code;
if (code == available) {
pixelStack[top++] = (byte) first;
code = old_code;
}
while (code > clear) {
pixelStack[top++] = suffix[code];
code = prefix[code];
}
first = ((int) suffix[code]) & 0xff;
// Add a new string to the string table,
if (available >= MAX_STACK_SIZE) {
break;
}
pixelStack[top++] = (byte) first;
prefix[available] = (short) old_code;
suffix[available] = (byte) first;
available++;
if (((available & code_mask) == 0) && (available < MAX_STACK_SIZE)) {
code_size++;
code_mask += available;
}
old_code = in_code;
}
// Pop a pixel off the pixel stack.
top--;
dstPixels[pi++] = pixelStack[top];
i++;
}
for (i = pi; i < npix; i++) {
dstPixels[i] = 0; // clear missing pixels
}
}
/**
* Returns true if an error was encountered during reading/decoding
*/
protected boolean err() {
return status != STATUS_OK;
}
/**
* Initializes or re-initializes reader
*/
protected void init() {
status = STATUS_OK;
frameCount = 0;
framePointer = -1;
frames = new ArrayList<GifFrame>();
gct = null;
}
/**
* Reads a single byte from the input stream.
*/
protected int read() {
int curByte = 0;
try {
curByte = (rawData.get() & 0xFF);
} catch (Exception e) {
status = STATUS_FORMAT_ERROR;
}
return curByte;
}
/**
* Reads next variable length block from input.
*
* @return number of bytes stored in "buffer"
*/
protected int readBlock() {
blockSize = read();
int n = 0;
if (blockSize > 0) {
try {
int count;
while (n < blockSize) {
count = blockSize - n;
rawData.get(block, n, count);
n += count;
}
} catch (Exception e) {
Log.w(TAG, "Error Reading Block", e);
status = STATUS_FORMAT_ERROR;
}
}
return n;
}
/**
* Reads color table as 256 RGB integer values
*
* @param ncolors int number of colors to read
* @return int array containing 256 colors (packed ARGB with full alpha)
*/
protected int[] readColorTable(int ncolors) {
int nbytes = 3 * ncolors;
int[] tab = null;
byte[] c = new byte[nbytes];
try {
rawData.get(c);
tab = new int[256]; // max size to avoid bounds checks
int i = 0;
int j = 0;
while (i < ncolors) {
int r = ((int) c[j++]) & 0xff;
int g = ((int) c[j++]) & 0xff;
int b = ((int) c[j++]) & 0xff;
tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
}
} catch (BufferUnderflowException e) {
Log.w(TAG, "Format Error Reading Color Table", e);
status = STATUS_FORMAT_ERROR;
}
return tab;
}
/**
* Main file parser. Reads GIF content blocks.
*/
protected void readContents() {
// read GIF file content blocks
boolean done = false;
while (!(done || err())) {
int code = read();
switch (code) {
case 0x2C: // image separator
readBitmap();
break;
case 0x21: // extension
code = read();
switch (code) {
case 0xf9: // graphics control extension
//Start a new frame
currentFrame = new GifFrame();
readGraphicControlExt();
break;
case 0xff: // application extension
readBlock();
String app = "";
for (int i = 0; i < 11; i++) {
app += (char) block[i];
}
if (app.equals("NETSCAPE2.0")) {
readNetscapeExt();
} else {
skip(); // don't care
}
break;
case 0xfe:// comment extension
skip();
break;
case 0x01:// plain text extension
skip();
break;
default: // uninteresting extension
skip();
}
break;
case 0x3b: // terminator
done = true;
break;
case 0x00: // bad byte, but keep going and see what happens break;
default:
status = STATUS_FORMAT_ERROR;
}
}
}
/**
* Reads GIF file header information.
*/
protected void readHeader() {
String id = "";
for (int i = 0; i < 6; i++) {
id += (char) read();
}
if (!id.startsWith("GIF")) {
status = STATUS_FORMAT_ERROR;
return;
}
readLSD();
if (gctFlag && !err()) {
gct = readColorTable(gctSize);
bgColor = gct[bgIndex];
}
}
/**
* Reads Graphics Control Extension values
*/
protected void readGraphicControlExt() {
read(); // block size
int packed = read(); // packed fields
currentFrame.dispose = (packed & 0x1c) >> 2; // disposal method
if (currentFrame.dispose == 0) {
currentFrame.dispose = 1; // elect to keep old image if discretionary
}
currentFrame.transparency = (packed & 1) != 0;
currentFrame.delay = readShort() * 10; // delay in milliseconds
currentFrame.transIndex = read(); // transparent color index
read(); // block terminator
}
/**
* Reads next frame image
*/
protected void readBitmap() {
currentFrame.ix = readShort(); // (sub)image position & size
currentFrame.iy = readShort();
currentFrame.iw = readShort();
currentFrame.ih = readShort();
int packed = read();
lctFlag = (packed & 0x80) != 0; // 1 - local color table flag interlace
lctSize = (int) Math.pow(2, (packed & 0x07) + 1);
// 3 - sort flag
// 4-5 - reserved lctSize = 2 << (packed & 7); // 6-8 - local color
// table size
currentFrame.interlace = (packed & 0x40) != 0;
if (lctFlag) {
currentFrame.lct = readColorTable(lctSize); // read table
} else {
currentFrame.lct = null; //No local color table
}
currentFrame.bufferFrameStart = rawData.position(); //Save this as the decoding position pointer
decodeBitmapData(null, mainPixels); // false decode pixel data to advance buffer
skip();
if (err()) {
return;
}
frameCount++;
frames.add(currentFrame); // add image to frame
}
/**
* Reads Logical Screen Descriptor
*/
protected void readLSD() {
// logical screen size
width = readShort();
height = readShort();
// packed fields
int packed = read();
gctFlag = (packed & 0x80) != 0; // 1 : global color table flag
// 2-4 : color resolution
// 5 : gct sort flag
gctSize = 2 << (packed & 7); // 6-8 : gct size
bgIndex = read(); // background color index
pixelAspect = read(); // pixel aspect ratio
//Now that we know the size, init scratch arrays
mainPixels = new byte[width * height];
mainScratch = new int[width * height];
copyScratch = new int[width * height];
previousImage = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
currentImage = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
}
/**
* Reads Netscape extenstion to obtain iteration count
*/
protected void readNetscapeExt() {
do {
readBlock();
if (block[0] == 1) {
// loop count sub-block
int b1 = ((int) block[1]) & 0xff;
int b2 = ((int) block[2]) & 0xff;
loopCount = (b2 << 8) | b1;
}
} while ((blockSize > 0) && !err());
}
/**
* Reads next 16-bit value, LSB first
*/
protected int readShort() {
// read 16-bit value
return rawData.getShort();
}
/**
* Skips variable length blocks up to and including next zero length block.
*/
protected void skip() {
do {
readBlock();
} while ((blockSize > 0) && !err());
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/InvalidParcelException.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.utilities;
/**
* Represents an exception that occurs when validating
* the results of a object created from a parcel
*/
public class InvalidParcelException extends Exception {
public InvalidParcelException() {
}
public InvalidParcelException(String detailMessage) {
super(detailMessage);
}
public InvalidParcelException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
}
public InvalidParcelException(Throwable throwable) {
super(throwable);
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/JSONObjectVisitor.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.utilities;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.Iterator;
public abstract class JSONObjectVisitor {
public static void walk(JSONObject object, JSONObjectVisitor visitor) {
visitor.visit(object);
}
public static void walk(JSONArray array, JSONObjectVisitor visitor) {
visitor.visit(array);
}
protected void visit(JSONObject object) {
for (Iterator<String> keys = object.keys(); keys.hasNext(); ) {
String key = keys.next();
Object value = object.opt(key);
if (value instanceof JSONObject) {
visit((JSONObject) value);
}
if (value instanceof JSONArray) {
visit((JSONArray) value);
}
}
}
protected void visit(JSONArray array) {
for (int index = 0; index < array.length(); index++) {
Object value = array.opt(index);
if (value instanceof JSONObject) {
visit((JSONObject) value);
}
if (value instanceof JSONArray) {
visit((JSONArray) value);
}
}
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/RoundedViewHelper.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.utilities;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.RectF;
import android.support.annotation.NonNull;
public class RoundedViewHelper {
public static final int TOP_LEFT = 1 << 0;
public static final int TOP_RIGHT = 1 << 1;
public static final int BOTTOM_LEFT = 1 << 2;
public static final int BOTTOM_RIGHT = 1 << 3;
public static final int ALL = TOP_LEFT | TOP_RIGHT | BOTTOM_LEFT | BOTTOM_RIGHT;
private final float cornerRadius;
private final int corners;
private final @NonNull RectF rect;
private final @NonNull Path clipPath;
/**
* Create a new {@link RoundedViewHelper}, with a specified corner radius and corners.
*
* @param context The context to help inside (usually an {@link Activity})
* @param cornerRadiusDip The corner radius to use.
* @param cornersMask The corners to automatically round.
*/
public RoundedViewHelper(@NonNull Context context, float cornerRadiusDip, int cornersMask) {
corners = cornersMask;
cornerRadius = context.getResources().getDisplayMetrics().density * cornerRadiusDip;
rect = new RectF();
clipPath = new Path();
}
public void onLayout(boolean changed, int l, int t, int r, int b) {
if (corners == 0 || !changed) {
return;
}
clipPath.reset();
rect.set(0, 0, (r - l), (b - t));
float[] radii = {0, 0, 0, 0, 0, 0, 0, 0};
if ((corners & TOP_LEFT) != 0) {
radii[0] = cornerRadius;
radii[1] = cornerRadius;
}
if ((corners & TOP_RIGHT) != 0) {
radii[2] = cornerRadius;
radii[3] = cornerRadius;
}
if ((corners & BOTTOM_LEFT) != 0) {
radii[4] = cornerRadius;
radii[5] = cornerRadius;
}
if ((corners & BOTTOM_RIGHT) != 0) {
radii[6] = cornerRadius;
radii[7] = cornerRadius;
}
clipPath.addRoundRect(rect, radii, Path.Direction.CW);
}
public void preDraw(Canvas canvas) {
if (corners == 0) {
return;
}
if (canvas.isOpaque()) {
canvas.saveLayerAlpha(0, 0, canvas.getWidth(), canvas.getHeight(), 255, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
}
canvas.clipPath(clipPath);
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/TransparentStateListDrawable.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.utilities;
import android.graphics.PixelFormat;
import android.graphics.drawable.StateListDrawable;
/**
* HACK:
* <p/>
* Due to an optimization in how canvases work, drawables which have an 'opaque' opacity
* setting actually still get immediately blitted to the screen, regardless of what canvas
* they're drawing in, which means they ignore the clip path of the canvas they're drawing into.
* <p/>
* We need a button that can be properly clipped, so we just simply extend the existing, working
* drawable, and make it transparent.
*/
public class TransparentStateListDrawable extends StateListDrawable {
@Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/Version.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.utilities;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.NoSuchElementException;
import java.util.Scanner;
/**
* Simple version class
*/
public class Version implements Comparable<Version> {
private final int major;
private final int minor;
private final int patch;
public Version(int major, int minor, int patch) {
this.major = major;
this.minor = minor;
this.patch = patch;
}
@Nullable
public static Version parse(String input) {
Scanner scanner = new Scanner(input);
scanner.useDelimiter("\\.");
int major = Integer.MIN_VALUE, minor = Integer.MIN_VALUE, patch = 0;
try {
major = scanner.nextInt();
minor = scanner.nextInt();
patch = scanner.nextInt();
} catch (NoSuchElementException ex) {
if (major == Integer.MIN_VALUE && minor == Integer.MIN_VALUE) {
return null;
}
}
return new Version(major, minor, patch);
}
public int getPatch() {
return patch;
}
public int getMinor() {
return minor;
}
public int getMajor() {
return major;
}
@Override
public String toString() {
return "Version{" + major + "." + minor + "." + patch + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Version version = (Version) o;
return
major == version.major &&
minor == version.minor &&
patch == version.patch;
}
@Override
public int hashCode() {
int result = major;
result = 31 * result + minor;
result = 31 * result + patch;
return result;
}
@Override
public int compareTo(@NonNull Version another) {
int major = this.major - another.major;
int minor = this.minor - another.minor;
int patch = this.patch - another.patch;
return major != 0 ? major :
minor != 0 ? minor :
patch;
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/view/ActionButton.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.support.annotation.NonNull;
import android.util.DisplayMetrics;
import android.util.StateSet;
import android.widget.Button;
import com.facebook.notifications.internal.configuration.ActionConfiguration;
import com.facebook.notifications.internal.content.Content;
import com.facebook.notifications.internal.utilities.TransparentStateListDrawable;
@SuppressLint("ViewConstructor")
public class ActionButton extends Button {
public enum Type {
Primary,
Secondary,
Dismiss
}
private final @NonNull ActionConfiguration configuration;
private final Type type;
public ActionButton(@NonNull Context context, @NonNull final ActionConfiguration config, @NonNull Type t, final float cornerRadius) {
super(context, null, android.R.attr.borderlessButtonStyle);
configuration = config;
type = t;
setTransformationMethod(null);
setPadding(0, 0, 0, 0);
final DisplayMetrics metrics = getResources().getDisplayMetrics();
final int backgroundColor = configuration.getBackgroundColor();
final int pressedColor;
final int borderWidth = Math.round(configuration.getBorderWidth() * metrics.density);
Content content = config.getContent();
if (content != null) {
content.applyTo(this);
}
float[] hsv = {0, 0, 0};
Color.colorToHSV(backgroundColor, hsv);
hsv[2] *= 0.5;
pressedColor = Color.HSVToColor(backgroundColor >> 24, hsv);
GradientDrawable backgroundGradient = new GradientDrawable() {{
setCornerRadius(cornerRadius * metrics.density);
setShape(GradientDrawable.RECTANGLE);
setStroke(borderWidth, configuration.getBorderColor());
setColor(backgroundColor);
}};
GradientDrawable pressedGradient = new GradientDrawable() {{
setCornerRadius(cornerRadius * metrics.density);
setShape(RECTANGLE);
setStroke(borderWidth, configuration.getBorderColor());
setColor(pressedColor);
}};
TransparentStateListDrawable stateListDrawable = new TransparentStateListDrawable();
stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pressedGradient);
stateListDrawable.addState(StateSet.WILD_CARD, backgroundGradient);
setBackgroundDrawable(stateListDrawable);
setWillNotDraw(false);
// ClipPath is not hardware-accelerated before 4.3
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
}
@NonNull
public ActionConfiguration getConfiguration() {
return configuration;
}
public Type getType() {
return type;
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/view/ActionsView.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.facebook.notifications.internal.asset.AssetManager;
import com.facebook.notifications.internal.configuration.ActionConfiguration;
import com.facebook.notifications.internal.configuration.ActionsConfiguration;
import com.facebook.notifications.internal.configuration.CardConfiguration;
import com.facebook.notifications.internal.utilities.RoundedViewHelper;
@SuppressWarnings("ResourceType")
@SuppressLint("ViewConstructor")
public class ActionsView extends RelativeLayout implements View.OnClickListener {
public interface Delegate {
void actionButtonClicked(ActionButton.Type type, @Nullable Uri actionUri);
}
private static final int ASSET_VIEW_ID = 0x78d5c27b;
private static final int BUTTONS_LAYOUT_ID = 0x7cc9a8c8;
private final @NonNull Delegate delegate;
private final @Nullable ActionsConfiguration configuration;
private final @NonNull RoundedViewHelper roundedViewHelper;
private final @NonNull AssetView assetView;
private final @NonNull LinearLayout buttonsLayout;
private final @NonNull ActionButton[] actionButtons;
public ActionsView(@NonNull Context context, @NonNull AssetManager assetManager, @NonNull Delegate del, @NonNull final CardConfiguration config) {
super(context);
delegate = del;
configuration = config.getActionsConfiguration();
if (configuration == null) {
roundedViewHelper = new RoundedViewHelper(context, 0, 0);
assetView = new AssetView(context, assetManager, null);
buttonsLayout = new LinearLayout(context);
actionButtons = new ActionButton[0];
return;
}
roundedViewHelper = new RoundedViewHelper(context, config.getCornerRadius(), getRoundedCorners(config));
assetView = new AssetView(context, assetManager, configuration.getBackground());
ActionConfiguration[] actions = configuration.getActions();
actionButtons = new ActionButton[actions.length];
final DisplayMetrics metrics = getResources().getDisplayMetrics();
int margin = Math.round(configuration.getContentInset() * metrics.density);
int topMargin = Math.round(configuration.getTopInset() * metrics.density);
int marginLeft = 0, marginTop = 0;
int buttonWidth = 0, buttonWeight = 0;
int buttonHeight = Math.round(configuration.getHeight() * metrics.density);
buttonsLayout = new LinearLayout(context);
switch (configuration.getLayoutStyle()) {
case Vertical:
buttonWidth = LayoutParams.MATCH_PARENT;
marginTop = margin;
buttonsLayout.setOrientation(LinearLayout.VERTICAL);
break;
case Horizontal:
buttonWeight = 1;
marginLeft = margin;
buttonsLayout.setOrientation(LinearLayout.HORIZONTAL);
break;
default:
throw new RuntimeException("Unknown layout style: " + configuration.getLayoutStyle());
}
boolean primary = true;
for (int actionIndex = 0; actionIndex < actions.length; actionIndex++) {
ActionConfiguration configuration = actions[actionIndex];
ActionButton.Type buttonType;
if (configuration.getActionUri() != null) {
buttonType = primary ? ActionButton.Type.Primary : ActionButton.Type.Secondary;
primary = false;
} else {
buttonType = ActionButton.Type.Dismiss;
}
actionButtons[actionIndex] = new ActionButton(context, actions[actionIndex], buttonType, this.configuration.getCornerRadius());
actionButtons[actionIndex].setOnClickListener(this);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(buttonWidth, buttonHeight);
layoutParams.weight = buttonWeight;
if (actionIndex > 0) {
layoutParams.setMargins(marginLeft, marginTop, 0, 0);
}
buttonsLayout.addView(actionButtons[actionIndex], layoutParams);
}
assetView.setId(ASSET_VIEW_ID);
buttonsLayout.setId(BUTTONS_LAYOUT_ID);
addView(assetView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) {{
addRule(ALIGN_TOP, BUTTONS_LAYOUT_ID);
addRule(ALIGN_BOTTOM, BUTTONS_LAYOUT_ID);
}});
buttonsLayout.setPadding(margin, topMargin, margin, margin);
addView(buttonsLayout, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
setWillNotDraw(false);
// ClipPath is not hardware-accelerated before 4.3
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
}
private static int getRoundedCorners(@NonNull CardConfiguration configuration) {
if (configuration.getActionsConfiguration() == null) {
return 0;
}
int corners = RoundedViewHelper.BOTTOM_LEFT | RoundedViewHelper.BOTTOM_RIGHT;
if (configuration.getHeroConfiguration() == null &&
configuration.getBodyConfiguration() == null) {
corners |= RoundedViewHelper.TOP_LEFT | RoundedViewHelper.TOP_RIGHT;
}
return corners;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
roundedViewHelper.onLayout(changed, l, t, r, b);
}
@Override
public void draw(Canvas canvas) {
roundedViewHelper.preDraw(canvas);
super.draw(canvas);
}
@Override
public void onClick(View v) {
ActionButton button = (ActionButton) v;
delegate.actionButtonClicked(button.getType(), button.getConfiguration().getActionUri());
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/view/AssetView.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.FrameLayout;
import com.facebook.notifications.internal.asset.Asset;
import com.facebook.notifications.internal.asset.AssetManager;
/**
* Implements a view that draws a asset.
*/
@SuppressLint("ViewConstructor")
public class AssetView extends FrameLayout {
private final @Nullable Asset asset;
public AssetView(@NonNull Context context, @NonNull AssetManager assetManager, @Nullable Asset asset) {
super(context);
this.asset = asset;
if (asset != null) {
View viewForAsset = assetManager.inflateView(asset, context);
addView(viewForAsset, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
}
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/view/BodyView.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.DisplayMetrics;
import android.widget.RelativeLayout;
import com.facebook.notifications.internal.asset.AssetManager;
import com.facebook.notifications.internal.configuration.ActionsConfiguration;
import com.facebook.notifications.internal.configuration.BodyConfiguration;
import com.facebook.notifications.internal.configuration.CardConfiguration;
import com.facebook.notifications.internal.content.ContentManager;
import com.facebook.notifications.internal.utilities.RoundedViewHelper;
@SuppressWarnings("ResourceType")
@SuppressLint("ViewConstructor")
public class BodyView extends RelativeLayout {
private final int RESOURCE_VIEW_ID = 0xa55d68f4;
private final int CONTENT_VIEW_ID = 0x2facb6c8;
private final @Nullable BodyConfiguration configuration;
private final @NonNull RoundedViewHelper roundedViewHelper;
private final @NonNull AssetView assetView;
private final @NonNull ContentView contentView;
public BodyView(@NonNull Context context, @NonNull AssetManager assetManager, @NonNull ContentManager contentManager, @NonNull CardConfiguration config) {
super(context);
configuration = config.getBodyConfiguration();
ActionsConfiguration actionsConfiguration = config.getActionsConfiguration();
ActionsConfiguration.ActionsStyle actionsStyle = actionsConfiguration != null
? actionsConfiguration.getStyle()
: ActionsConfiguration.ActionsStyle.Detached;
int corners = actionsStyle == ActionsConfiguration.ActionsStyle.Detached
? RoundedViewHelper.BOTTOM_LEFT | RoundedViewHelper.BOTTOM_RIGHT
: 0;
if (config.getHeroConfiguration() == null && configuration != null) {
corners |= RoundedViewHelper.TOP_LEFT | RoundedViewHelper.TOP_RIGHT;
}
roundedViewHelper = new RoundedViewHelper(context, config.getCornerRadius(), corners);
if (configuration == null) {
assetView = new AssetView(context, assetManager, null);
contentView = new ContentView(context, contentManager, null);
return;
}
assetView = new AssetView(context, assetManager, configuration.getBackground());
contentView = new ContentView(context, contentManager, configuration.getContent());
assetView.setId(RESOURCE_VIEW_ID);
contentView.setId(CONTENT_VIEW_ID);
DisplayMetrics metrics = getResources().getDisplayMetrics();
final int padding = Math.round(config.getContentInset() * metrics.density);
contentView.setPadding(padding, padding, padding, padding);
addView(assetView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) {{
addRule(ALIGN_TOP, CONTENT_VIEW_ID);
addRule(ALIGN_BOTTOM, CONTENT_VIEW_ID);
}});
addView(contentView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
setWillNotDraw(false);
// ClipPath is not hardware-accelerated before 4.3
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
roundedViewHelper.onLayout(changed, l, t, r, b);
}
@Override
public void draw(Canvas canvas) {
roundedViewHelper.preDraw(canvas);
super.draw(canvas);
}
}
================================================
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/view/CardView.java
================================================
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.facebook.notifications.internal.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.widget.LinearLayout;
import com.facebook.notifications.internal.asset.AssetManager;
import com.facebook.notifications.internal.configuration.CardConfiguration;
import com.facebook.notifications.internal.configuration.HeroConfiguration;
import com.facebook.notifications.internal.content.ContentManager;
/**
* This class is the start of the Push Card hierarchy.
* <p/>
* It contains a {@link HeroView}, a {@link BodyView}, and a {@link ActionsView}.
*/
// As this class will only be created via code, suppress the following warning.
@SuppressLint("ViewConstructor")
public class CardView extends LinearLayout {
private final @NonNull CardConfiguration configuration;
private final @NonNull HeroView heroView;
private final @NonNull BodyView bodyView;
private final @NonNull ActionsView actionsView;
/**
* Create a new card view, from the given configuration.
*
* @param context The context of the view (usually an activity).
* @param config The configuration of the push card to display.
*/
public CardView(
@NonNull Context context,
@NonNull AssetManager assetManager,
@NonNull ContentManager contentManager,
@NonNull ActionsView.Delegate actionsDelegate,
@NonNull CardConfiguration config
) {
super(context);
configuration = config;
heroView = new HeroView(context, assetManager, contentManager, configuration);
bodyView = new BodyView(context, assetManager, contentManager, configuration);
actionsView = new ActionsView(context, assetManager, actionsDelegate, configuration);
setOrientation(VERTICAL);
setGravity(Gravity.CENTER_VERTICAL);
addView(heroView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
addView(bodyView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
addView(actionsView, new LayoutParams(LayoutParams.MATCH_PARENT,
gitextract_ihx0v_r0/
├── .gitignore
├── .gitmodules
├── .travis.yml
├── Android/
│ ├── .gitignore
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradle.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── notifications/
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src/
│ │ └── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── com/
│ │ └── facebook/
│ │ └── notifications/
│ │ ├── NotificationCardResult.java
│ │ ├── NotificationsManager.java
│ │ └── internal/
│ │ ├── activity/
│ │ │ └── CardActivity.java
│ │ ├── appevents/
│ │ │ └── AppEventsLogger.java
│ │ ├── asset/
│ │ │ ├── Asset.java
│ │ │ ├── AssetManager.java
│ │ │ ├── ParcelableAssetHandler.java
│ │ │ ├── cache/
│ │ │ │ ├── CacheOperation.java
│ │ │ │ ├── ContentCache.java
│ │ │ │ ├── ContentDownloader.java
│ │ │ │ └── DiskCache.java
│ │ │ └── handlers/
│ │ │ ├── BitmapAssetHandler.java
│ │ │ ├── ColorAssetHandler.java
│ │ │ └── GifAssetHandler.java
│ │ ├── configuration/
│ │ │ ├── ActionConfiguration.java
│ │ │ ├── ActionsConfiguration.java
│ │ │ ├── BodyConfiguration.java
│ │ │ ├── CardConfiguration.java
│ │ │ └── HeroConfiguration.java
│ │ ├── content/
│ │ │ ├── Content.java
│ │ │ ├── ContentManager.java
│ │ │ └── TextContent.java
│ │ ├── utilities/
│ │ │ ├── EnumCreator.java
│ │ │ ├── FontUtilities.java
│ │ │ ├── GifDecoder.java
│ │ │ ├── InvalidParcelException.java
│ │ │ ├── JSONObjectVisitor.java
│ │ │ ├── RoundedViewHelper.java
│ │ │ ├── TransparentStateListDrawable.java
│ │ │ └── Version.java
│ │ └── view/
│ │ ├── ActionButton.java
│ │ ├── ActionsView.java
│ │ ├── AssetView.java
│ │ ├── BodyView.java
│ │ ├── CardView.java
│ │ ├── ContentView.java
│ │ ├── GifView.java
│ │ └── HeroView.java
│ ├── notifications-example/
│ │ ├── build.gradle
│ │ ├── google-services.json
│ │ ├── proguard-rules.pro
│ │ └── src/
│ │ └── main/
│ │ ├── AndroidManifest.xml
│ │ ├── assets/
│ │ │ ├── example1.json
│ │ │ ├── example2.json
│ │ │ ├── example3.json
│ │ │ ├── example4.json
│ │ │ └── example5.json
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── facebook/
│ │ │ └── notifications/
│ │ │ └── sample/
│ │ │ ├── MainActivity.java
│ │ │ ├── MyGcmListenerService.java
│ │ │ ├── MyInstanceIDListenerService.java
│ │ │ └── RegistrationIntentService.java
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ └── content_main.xml
│ │ ├── menu/
│ │ │ └── menu_main.xml
│ │ ├── values/
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── values-v21/
│ │ │ └── styles.xml
│ │ └── values-w820dp/
│ │ └── dimens.xml
│ └── settings.gradle
├── CHANGELOG-Android.md
├── CHANGELOG-iOS.md
├── CONTRIBUTING.md
├── FBNotifications.podspec
├── Format/
│ ├── Format-1.0.md
│ └── Format-Changelog.md
├── LICENSE
├── LICENSE-specification
├── README.md
└── iOS/
├── .gitignore
├── FBNotifications/
│ ├── Configurations/
│ │ ├── FBNotifications-iOS-Dynamic.xcconfig
│ │ └── FBNotifications-iOS.xcconfig
│ ├── FBNotifications/
│ │ ├── FBNCardViewController.h
│ │ ├── FBNCardViewController.m
│ │ ├── FBNConstants.h
│ │ ├── FBNConstants.m
│ │ ├── FBNotifications.h
│ │ ├── FBNotificationsManager.h
│ │ ├── FBNotificationsManager.m
│ │ ├── Internal/
│ │ │ ├── AppEvents/
│ │ │ │ ├── FBNCardAppEventsLogger.h
│ │ │ │ └── FBNCardAppEventsLogger.m
│ │ │ ├── Asset/
│ │ │ │ ├── Cache/
│ │ │ │ │ ├── FBNAssetContentCache.h
│ │ │ │ │ ├── FBNAssetContentCache.m
│ │ │ │ │ ├── FBNAssetContentCacheOperation.h
│ │ │ │ │ └── FBNAssetContentCacheOperation.m
│ │ │ │ ├── Color/
│ │ │ │ │ ├── FBNColorAsset.h
│ │ │ │ │ ├── FBNColorAsset.m
│ │ │ │ │ ├── FBNColorAssetController.h
│ │ │ │ │ ├── FBNColorAssetController.m
│ │ │ │ │ ├── FBNColorAssetViewController.h
│ │ │ │ │ └── FBNColorAssetViewController.m
│ │ │ │ ├── Common/
│ │ │ │ │ ├── FBNAsset.h
│ │ │ │ │ └── FBNAssetController.h
│ │ │ │ ├── FBNAssetsController.h
│ │ │ │ ├── FBNAssetsController.m
│ │ │ │ ├── GIF/
│ │ │ │ │ ├── FBNGIFAsset.h
│ │ │ │ │ ├── FBNGIFAsset.m
│ │ │ │ │ ├── FBNGIFAssetController.h
│ │ │ │ │ ├── FBNGIFAssetController.m
│ │ │ │ │ ├── FBNIGIFAssetViewController.h
│ │ │ │ │ └── FBNIGIFAssetViewController.m
│ │ │ │ └── Image/
│ │ │ │ ├── FBNImageAsset.h
│ │ │ │ ├── FBNImageAsset.m
│ │ │ │ ├── FBNImageAssetController.h
│ │ │ │ ├── FBNImageAssetController.m
│ │ │ │ ├── FBNImageAssetViewController.h
│ │ │ │ └── FBNImageAssetViewController.m
│ │ │ ├── FBNCardViewController_Internal.h
│ │ │ ├── Models/
│ │ │ │ ├── Actions/
│ │ │ │ │ ├── FBNCardActionConfiguration.h
│ │ │ │ │ ├── FBNCardActionConfiguration.m
│ │ │ │ │ ├── FBNCardActionsConfiguration.h
│ │ │ │ │ ├── FBNCardActionsConfiguration.m
│ │ │ │ │ ├── FBNCardActionsStyle.h
│ │ │ │ │ ├── FBNCardActionsStyle.m
│ │ │ │ │ └── FBNCardButtonAction.h
│ │ │ │ ├── Body/
│ │ │ │ │ ├── FBNCardBodyConfiguration.h
│ │ │ │ │ └── FBNCardBodyConfiguration.m
│ │ │ │ ├── Card/
│ │ │ │ │ ├── FBNCardConfiguration.h
│ │ │ │ │ ├── FBNCardConfiguration.m
│ │ │ │ │ ├── FBNCardDisplayOptions.h
│ │ │ │ │ ├── FBNCardDisplayOptions.m
│ │ │ │ │ ├── FBNCardSize.h
│ │ │ │ │ └── FBNCardSize.m
│ │ │ │ ├── Components/
│ │ │ │ │ ├── FBNAnimatedImage.h
│ │ │ │ │ ├── FBNAnimatedImage.m
│ │ │ │ │ ├── FBNCardBackground.h
│ │ │ │ │ ├── FBNCardBackground.m
│ │ │ │ │ ├── FBNCardColor.h
│ │ │ │ │ ├── FBNCardColor.m
│ │ │ │ │ ├── FBNCardFont.h
│ │ │ │ │ ├── FBNCardFont.m
│ │ │ │ │ ├── FBNCardTextAlignment.h
│ │ │ │ │ ├── FBNCardTextAlignment.m
│ │ │ │ │ ├── FBNCardTextContent.h
│ │ │ │ │ └── FBNCardTextContent.m
│ │ │ │ └── Hero/
│ │ │ │ ├── FBNCardHeroConfiguration.h
│ │ │ │ └── FBNCardHeroConfiguration.m
│ │ │ ├── Utilities/
│ │ │ │ ├── FBNCardError.h
│ │ │ │ ├── FBNCardError.m
│ │ │ │ ├── FBNCardHash.h
│ │ │ │ ├── FBNCardHash.m
│ │ │ │ ├── FBNCardPayload.h
│ │ │ │ ├── FBNCardPayload.m
│ │ │ │ ├── FBNCardViewUtilities.h
│ │ │ │ ├── FBNCardViewUtilities.m
│ │ │ │ └── FBNContentSizeProvider.h
│ │ │ └── Views/
│ │ │ ├── Components/
│ │ │ │ ├── FBNCardDismissButton.h
│ │ │ │ ├── FBNCardDismissButton.m
│ │ │ │ ├── FBNCardLabel.h
│ │ │ │ └── FBNCardLabel.m
│ │ │ ├── FBNCardActionButton.h
│ │ │ ├── FBNCardActionButton.m
│ │ │ ├── FBNCardActionsViewController.h
│ │ │ ├── FBNCardActionsViewController.m
│ │ │ ├── FBNCardBodyViewController.h
│ │ │ ├── FBNCardBodyViewController.m
│ │ │ ├── FBNCardHeroViewController.h
│ │ │ └── FBNCardHeroViewController.m
│ │ └── Resources/
│ │ └── Info.plist
│ └── FBNotifications.xcodeproj/
│ ├── project.pbxproj
│ └── xcshareddata/
│ └── xcschemes/
│ ├── FBNotifications-iOS-Dynamic.xcscheme
│ └── FBNotifications-iOS.xcscheme
├── FBNotifications.xcworkspace/
│ └── contents.xcworkspacedata
├── FBNotificationsExample/
│ ├── .gitignore
│ ├── FBNotificationsExample/
│ │ ├── Resources/
│ │ │ ├── Assets.xcassets/
│ │ │ │ └── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ ├── Base.lproj/
│ │ │ │ ├── LaunchScreen.storyboard
│ │ │ │ └── Main.storyboard
│ │ │ ├── Examples/
│ │ │ │ ├── example1.json
│ │ │ │ ├── example2.json
│ │ │ │ ├── example3.json
│ │ │ │ ├── example4.json
│ │ │ │ └── example5.json
│ │ │ └── Info.plist
│ │ └── Source/
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── ViewController.h
│ │ ├── ViewController.m
│ │ └── main.m
│ └── FBNotificationsExample.xcodeproj/
│ ├── project.pbxproj
│ └── xcshareddata/
│ └── xcschemes/
│ └── FBNotificationsExample.xcscheme
└── Rakefile
SYMBOL INDEX (391 symbols across 45 files)
FILE: Android/notifications-example/src/main/java/com/facebook/notifications/sample/MainActivity.java
class MainActivity (line 41) | public class MainActivity extends AppCompatActivity {
method onCreate (line 44) | @Override
method onNewIntent (line 60) | @Override
method onActivityResult (line 66) | @Override
method getBundle (line 81) | @NonNull
method showExample (line 109) | public void showExample(View view) {
method showNotification (line 115) | public void showNotification(View view) {
FILE: Android/notifications-example/src/main/java/com/facebook/notifications/sample/MyGcmListenerService.java
class MyGcmListenerService (line 31) | public class MyGcmListenerService extends GcmListenerService {
method onMessageReceived (line 34) | @Override
FILE: Android/notifications-example/src/main/java/com/facebook/notifications/sample/MyInstanceIDListenerService.java
class MyInstanceIDListenerService (line 25) | public class MyInstanceIDListenerService extends InstanceIDListenerServi...
method onTokenRefresh (line 26) | @Override
FILE: Android/notifications-example/src/main/java/com/facebook/notifications/sample/RegistrationIntentService.java
class RegistrationIntentService (line 28) | public class RegistrationIntentService extends IntentService {
method RegistrationIntentService (line 31) | public RegistrationIntentService() {
method onHandleIntent (line 35) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/NotificationCardResult.java
class NotificationCardResult (line 26) | public class NotificationCardResult implements Parcelable {
method createFromParcel (line 28) | @Override
method newArray (line 33) | @Override
method NotificationCardResult (line 41) | public NotificationCardResult(@Nullable Uri actionUri) {
method NotificationCardResult (line 45) | private NotificationCardResult(Parcel parcel) {
method getActionUri (line 49) | @Nullable
method describeContents (line 54) | @Override
method writeToParcel (line 59) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/NotificationsManager.java
class NotificationsManager (line 51) | public final class NotificationsManager {
type PrepareCallback (line 55) | public interface PrepareCallback {
method onPrepared (line 56) | void onPrepared(@NonNull Intent presentationIntent);
method onError (line 58) | void onError(@NonNull Exception exception);
type NotificationExtender (line 64) | public interface NotificationExtender {
method extendNotification (line 65) | Notification.Builder extendNotification(@NonNull Notification.Builde...
method NotificationsManager (line 105) | private NotificationsManager() {
method getPushJSON (line 108) | @Nullable
method getCardJSON (line 117) | @Nullable
method intentForBundle (line 127) | @Nullable
method getAssetManager (line 156) | @NonNull
method getContentManager (line 163) | @NonNull
method canPresentCard (line 175) | public static boolean canPresentCard(@NonNull Bundle notificationBundl...
method presentCard (line 186) | public static boolean presentCard(@NonNull Activity activity, @NonNull...
method prepareCard (line 220) | public static void prepareCard(
method handleActivityResult (line 295) | public static NotificationCardResult handleActivityResult(int requestC...
method presentNotification (line 328) | public static boolean presentNotification(
method presentNotification (line 360) | public static boolean presentNotification(
method presentCardFromNotification (line 450) | public static boolean presentCardFromNotification(@NonNull Activity ac...
method presentCardFromNotification (line 462) | public static boolean presentCardFromNotification(@NonNull Activity ac...
method registerAssetHandler (line 479) | private static void registerAssetHandler(
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/activity/CardActivity.java
class CardActivity (line 50) | public class CardActivity extends Activity implements ActionsView.Delega...
method onCreate (line 87) | @Override
method onDestroy (line 126) | @Override
method beginLoadingContent (line 139) | private void beginLoadingContent() {
method displayConfiguration (line 190) | private void displayConfiguration(CardConfiguration configuration) {
method onBackPressed (line 200) | @Override
method actionButtonClicked (line 210) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/appevents/AppEventsLogger.java
class AppEventsLogger (line 37) | public class AppEventsLogger {
method AppEventsLogger (line 46) | public AppEventsLogger(@NonNull Context context) {
method getCampaignIdentifier (line 62) | @Nullable
method getAppEventName (line 67) | @NonNull
method logPushOpen (line 81) | public void logPushOpen(@Nullable String campaignIdentifier) {
method logButtonAction (line 85) | public void logButtonAction(ActionButton.Type action, @Nullable String...
method logEvent (line 89) | private void logEvent(@NonNull String eventName, @Nullable String camp...
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/Asset.java
type Asset (line 29) | public interface Asset extends Parcelable {
method getType (line 37) | @NonNull
method validate (line 45) | void validate() throws InvalidParcelException;
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/AssetManager.java
class AssetManager (line 46) | public class AssetManager implements Parcelable {
type AssetCache (line 50) | public interface AssetCache {
method getCachedFile (line 58) | File getCachedFile(URL url);
type AssetHandler (line 67) | public interface AssetHandler<AssetType extends Asset> {
method getCacheURLs (line 75) | @Nullable
method createAsset (line 86) | @Nullable
method createView (line 97) | @NonNull
type CacheCompletionCallback (line 104) | public interface CacheCompletionCallback {
method onCacheCompleted (line 110) | void onCacheCompleted(@NonNull JSONObject payload);
method createFromParcel (line 114) | @Override
method newArray (line 127) | @Override
method AssetManager (line 137) | public AssetManager() {
method AssetManager (line 145) | public AssetManager(AssetManager other) {
method AssetManager (line 149) | public AssetManager(@NonNull Parcel parcel) {
method setContext (line 158) | public void setContext(@NonNull Context context) {
method validate (line 166) | private void validate() throws InvalidParcelException {
method registerHandler (line 172) | public void registerHandler(@NonNull String assetType, @NonNull AssetH...
method inflateAsset (line 176) | @Nullable
method inflateView (line 195) | @NonNull
method cachePayload (line 214) | public void cachePayload(final @NonNull JSONObject payload, final @Non...
method clearCache (line 232) | public void clearCache(@NonNull JSONObject payload) {
method stopCaching (line 243) | public void stopCaching() {
method getCacheURLs (line 250) | @NonNull
method describeContents (line 284) | @Override
method writeToParcel (line 289) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/ParcelableAssetHandler.java
class ParcelableAssetHandler (line 38) | class ParcelableAssetHandler implements AssetManager.AssetHandler<Asset>...
method createFromParcel (line 40) | @Override
method newArray (line 45) | @Override
method ParcelableAssetHandler (line 54) | @SuppressWarnings("unchecked")
method ParcelableAssetHandler (line 60) | @SuppressWarnings("unchecked")
method validate (line 83) | public void validate() throws InvalidParcelException {
method getCacheURLs (line 93) | @Nullable
method createAsset (line 102) | @Nullable
method createView (line 111) | @NonNull
method describeContents (line 120) | @Override
method writeToParcel (line 125) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/cache/CacheOperation.java
class CacheOperation (line 29) | class CacheOperation implements ContentDownloader.DownloadCallback {
method CacheOperation (line 35) | public CacheOperation(@NonNull Set<URL> urlsToCache, @NonNull ContentC...
method getUrlsToCache (line 42) | @NonNull
method getCompletion (line 47) | @NonNull
method onResourceDownloaded (line 52) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/cache/ContentCache.java
class ContentCache (line 43) | public class ContentCache implements AssetManager.AssetCache {
type CompletionCallback (line 44) | public interface CompletionCallback {
method onCacheCompleted (line 45) | void onCacheCompleted(@NonNull Set<URL> urlsToCache);
method ContentCache (line 61) | public ContentCache(@NonNull Context context) {
method getCacheKey (line 75) | @NonNull
method stop (line 98) | public void stop() {
method getContext (line 110) | @NonNull
method cache (line 115) | public void cache(@NonNull Set<URL> urlsToCache, @NonNull CompletionCa...
method clear (line 161) | public void clear(@NonNull Set<URL> urlsToClear) {
method getCachedFile (line 173) | @Nullable
method hasCachedData (line 183) | private boolean hasCachedData(@NonNull String cacheKey) {
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/cache/ContentDownloader.java
class ContentDownloader (line 35) | class ContentDownloader implements Runnable {
type DownloadCallback (line 36) | public interface DownloadCallback {
method onResourceDownloaded (line 37) | void onResourceDownloaded(@NonNull URL url, @Nullable File file);
type DownloadOperation (line 40) | private interface DownloadOperation extends Runnable {
method ContentDownloader (line 46) | public ContentDownloader() {
method run (line 50) | @Override
method downloadAsync (line 63) | public void downloadAsync(final @NonNull URL url, final @NonNull File ...
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/cache/DiskCache.java
class DiskCache (line 30) | class DiskCache {
method DiskCache (line 35) | public DiskCache(@NonNull Context context) {
method remove (line 39) | public void remove(final @NonNull String key) {
method fetch (line 46) | @NonNull
method getCacheKeys (line 51) | @NonNull
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/handlers/BitmapAssetHandler.java
class BitmapAssetHandler (line 51) | public class BitmapAssetHandler implements AssetManager.AssetHandler<Bit...
class BitmapAsset (line 55) | static class BitmapAsset implements Asset {
method createFromParcel (line 57) | @Override
method newArray (line 62) | @Override
method BitmapAsset (line 70) | private BitmapAsset(@NonNull File createdFrom) {
method BitmapAsset (line 74) | private BitmapAsset(@NonNull Parcel parcel) {
method decodeBitmap (line 78) | @Nullable
method getCreatedFrom (line 106) | @NonNull
method getBitmap (line 111) | @NonNull
method getType (line 122) | @NonNull
method validate (line 128) | @Override
method describeContents (line 139) | @Override
method writeToParcel (line 144) | @Override
method getCacheURLs (line 153) | @Nullable
method createAsset (line 169) | @Nullable
method createView (line 189) | @NonNull
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/handlers/ColorAssetHandler.java
class ColorAssetHandler (line 42) | public class ColorAssetHandler implements AssetManager.AssetHandler<Colo...
class ColorAsset (line 46) | static class ColorAsset implements Asset {
method createFromParcel (line 48) | @Override
method newArray (line 53) | @Override
method ColorAsset (line 61) | private ColorAsset(int color) {
method ColorAsset (line 65) | private ColorAsset(@NonNull Parcel parcel) {
method getColor (line 69) | public int getColor() {
method getType (line 73) | @NonNull
method validate (line 79) | @Override
method describeContents (line 84) | @Override
method writeToParcel (line 89) | @Override
class ColorView (line 95) | @SuppressWarnings("ViewConstructor")
method ColorView (line 97) | public ColorView(Context context, int color) {
method fromRGBAHex (line 112) | public static int fromRGBAHex(@Nullable String input) {
method getCacheURLs (line 128) | @Nullable
method createAsset (line 134) | @Nullable
method createView (line 145) | @NonNull
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/asset/handlers/GifAssetHandler.java
class GifAssetHandler (line 49) | public class GifAssetHandler implements AssetManager.AssetHandler<GifAss...
class GifAsset (line 53) | static class GifAsset implements Asset {
method createFromParcel (line 55) | @Override
method newArray (line 60) | @Override
method GifAsset (line 68) | private GifAsset(@NonNull File createdFrom) {
method GifAsset (line 72) | private GifAsset(@NonNull Parcel source) {
method decodeGif (line 76) | @Nullable
method getCreatedFrom (line 115) | @NonNull
method getDecoder (line 120) | @NonNull
method getType (line 131) | @NonNull
method validate (line 137) | @Override
method describeContents (line 148) | @Override
method writeToParcel (line 153) | @Override
method getCacheURLs (line 162) | @Nullable
method createAsset (line 178) | @Nullable
method createView (line 198) | @NonNull
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/ActionConfiguration.java
class ActionConfiguration (line 34) | public class ActionConfiguration implements Parcelable {
method createFromParcel (line 36) | @Override
method newArray (line 41) | @Override
method ActionConfiguration (line 54) | public ActionConfiguration(JSONObject json) throws JSONException {
method ActionConfiguration (line 67) | private ActionConfiguration(Parcel source) {
method getBackgroundColor (line 79) | public int getBackgroundColor() {
method getBorderColor (line 83) | public int getBorderColor() {
method getBorderWidth (line 87) | public float getBorderWidth() {
method getContent (line 91) | @Nullable
method getActionUri (line 96) | @Nullable
method describeContents (line 101) | @Override
method writeToParcel (line 106) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/ActionsConfiguration.java
class ActionsConfiguration (line 35) | public class ActionsConfiguration implements Parcelable {
type ActionsStyle (line 36) | public enum ActionsStyle implements Parcelable {
method parse (line 42) | public static ActionsStyle parse(String input) {
method describeContents (line 53) | @Override
method writeToParcel (line 58) | @Override
type ActionsLayoutStyle (line 64) | public enum ActionsLayoutStyle implements Parcelable {
method parse (line 70) | public static ActionsLayoutStyle parse(String input) {
method describeContents (line 81) | @Override
method writeToParcel (line 86) | @Override
method createFromParcel (line 93) | @Override
method newArray (line 98) | @Override
method ActionsConfiguration (line 115) | private ActionsConfiguration(@NonNull JSONObject json, @NonNull AssetM...
method ActionsConfiguration (line 135) | private ActionsConfiguration(Parcel source) {
method fromJSON (line 151) | @Nullable
method validate (line 159) | public void validate() throws InvalidParcelException {
method getStyle (line 165) | public ActionsStyle getStyle() {
method getLayoutStyle (line 169) | public ActionsLayoutStyle getLayoutStyle() {
method getBackground (line 173) | public Asset getBackground() {
method getTopInset (line 177) | public float getTopInset() {
method getContentInset (line 181) | public float getContentInset() {
method getCornerRadius (line 185) | public float getCornerRadius() {
method getActions (line 189) | @NonNull
method getHeight (line 194) | public float getHeight() {
method describeContents (line 198) | @Override
method writeToParcel (line 203) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/BodyConfiguration.java
class BodyConfiguration (line 36) | public class BodyConfiguration implements Parcelable {
method createFromParcel (line 38) | @Override
method newArray (line 43) | @Override
method BodyConfiguration (line 52) | private BodyConfiguration(
method BodyConfiguration (line 66) | private BodyConfiguration(Parcel source) {
method fromJSON (line 73) | @Nullable
method validate (line 81) | public void validate() throws InvalidParcelException {
method getBackground (line 87) | @Nullable
method getContent (line 92) | @Nullable
method describeContents (line 97) | @Override
method writeToParcel (line 102) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/CardConfiguration.java
class CardConfiguration (line 36) | public class CardConfiguration implements Parcelable {
type CardSize (line 37) | public enum CardSize implements Parcelable {
method parse (line 45) | public static CardSize parse(String input) {
method describeContents (line 58) | @Override
method writeToParcel (line 63) | @Override
method createFromParcel (line 78) | @Override
method newArray (line 91) | @Override
method CardConfiguration (line 97) | public CardConfiguration(@NonNull JSONObject jsonObject, @NonNull Asse...
method CardConfiguration (line 109) | private CardConfiguration(@NonNull Parcel source) {
method validate (line 123) | public void validate() throws InvalidParcelException {
method getCardSize (line 137) | public CardSize getCardSize() {
method getCornerRadius (line 141) | public float getCornerRadius() {
method getContentInset (line 145) | public float getContentInset() {
method getBackdropColor (line 149) | public int getBackdropColor() {
method getHeroConfiguration (line 153) | @Nullable
method getBodyConfiguration (line 158) | @Nullable
method getActionsConfiguration (line 163) | @Nullable
method describeContents (line 168) | @Override
method writeToParcel (line 173) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/HeroConfiguration.java
class HeroConfiguration (line 36) | public class HeroConfiguration implements Parcelable {
method createFromParcel (line 38) | @Override
method newArray (line 43) | @Override
method HeroConfiguration (line 55) | private HeroConfiguration(
method HeroConfiguration (line 72) | private HeroConfiguration(Parcel parcel) {
method fromJSON (line 91) | @Nullable
method validate (line 103) | public void validate() throws InvalidParcelException {
method getHeight (line 109) | public float getHeight() {
method getBackground (line 113) | @Nullable
method getContent (line 118) | @Nullable
method getContentVerticalAlignment (line 123) | public Content.VerticalAlignment getContentVerticalAlignment() {
method describeContents (line 127) | @Override
method writeToParcel (line 132) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/content/Content.java
type Content (line 28) | public interface Content extends Parcelable {
type VerticalAlignment (line 29) | enum VerticalAlignment implements Parcelable {
method parse (line 36) | public static VerticalAlignment parse(String input) {
method describeContents (line 49) | @Override
method writeToParcel (line 54) | @Override
method applyTo (line 64) | void applyTo(@NonNull View view);
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/content/ContentManager.java
class ContentManager (line 31) | public class ContentManager implements Parcelable {
method createFromParcel (line 33) | @Override
method newArray (line 38) | @Override
method ContentManager (line 45) | public ContentManager() {
method ContentManager (line 48) | public ContentManager(ContentManager other) {
method ContentManager (line 51) | public ContentManager(@NonNull Parcel source) {
method setContext (line 54) | public void setContext(@NonNull Context context) {
method validate (line 57) | public void validate() throws InvalidParcelException {
method describeContents (line 60) | @Override
method writeToParcel (line 65) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/content/TextContent.java
class TextContent (line 37) | public class TextContent implements Content {
type Alignment (line 38) | public enum Alignment implements Parcelable {
method parse (line 45) | public static Alignment parse(@NonNull String input) {
method describeContents (line 58) | @Override
method writeToParcel (line 63) | @Override
method createFromParcel (line 70) | @Override
method newArray (line 75) | @Override
method TextContent (line 89) | public TextContent(@NonNull JSONObject json) throws JSONException {
method TextContent (line 99) | private TextContent(Parcel source) {
method applyTo (line 109) | @Override
method getText (line 139) | @NonNull
method getTextColor (line 144) | public int getTextColor() {
method getTypeface (line 148) | @Nullable
method getTypefaceSize (line 153) | public float getTypefaceSize() {
method getTextAlignment (line 157) | public Alignment getTextAlignment() {
method describeContents (line 161) | @Override
method writeToParcel (line 166) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/EnumCreator.java
class EnumCreator (line 26) | public class EnumCreator<T extends Enum> implements Parcelable.Creator<T> {
method EnumCreator (line 30) | public EnumCreator(Class<T> kls, T[] values) {
method createFromParcel (line 35) | @Override
method newArray (line 40) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/FontUtilities.java
class FontUtilities (line 24) | public class FontUtilities {
method parseFont (line 25) | public static @Nullable Typeface parseFont(@Nullable String fontName) {
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/GifDecoder.java
class GifDecoder (line 53) | @SuppressWarnings("ALL")
class GifFrame (line 134) | private static class GifFrame {
method advance (line 154) | public void advance() {
method recycle (line 162) | public void recycle() {
method getDelay (line 194) | public int getDelay(int n) {
method getNextDelay (line 205) | public int getNextDelay() {
method getFrameCount (line 218) | public int getFrameCount() {
method getCurrentFrameIndex (line 227) | public int getCurrentFrameIndex() {
method getLoopCount (line 236) | public int getLoopCount() {
method getWidth (line 240) | public int getWidth() {
method getHeight (line 244) | public int getHeight() {
method getNextFrame (line 253) | public Bitmap getNextFrame() {
method read (line 297) | public int read(InputStream is, int contentLength) {
method read (line 333) | public int read(byte[] data) {
method setPixels (line 358) | protected void setPixels(int frameIndex) {
method decodeBitmapData (line 457) | protected void decodeBitmapData(GifFrame frame, byte[] dstPixels) {
method err (line 575) | protected boolean err() {
method init (line 582) | protected void init() {
method read (line 593) | protected int read() {
method readBlock (line 608) | protected int readBlock() {
method readColorTable (line 634) | protected int[] readColorTable(int ncolors) {
method readContents (line 662) | protected void readContents() {
method readHeader (line 714) | protected void readHeader() {
method readGraphicControlExt (line 733) | protected void readGraphicControlExt() {
method readBitmap (line 749) | protected void readBitmap() {
method readLSD (line 783) | protected void readLSD() {
method readNetscapeExt (line 808) | protected void readNetscapeExt() {
method readShort (line 823) | protected int readShort() {
method skip (line 831) | protected void skip() {
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/InvalidParcelException.java
class InvalidParcelException (line 25) | public class InvalidParcelException extends Exception {
method InvalidParcelException (line 26) | public InvalidParcelException() {
method InvalidParcelException (line 29) | public InvalidParcelException(String detailMessage) {
method InvalidParcelException (line 33) | public InvalidParcelException(String detailMessage, Throwable throwabl...
method InvalidParcelException (line 37) | public InvalidParcelException(Throwable throwable) {
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/JSONObjectVisitor.java
class JSONObjectVisitor (line 26) | public abstract class JSONObjectVisitor {
method walk (line 27) | public static void walk(JSONObject object, JSONObjectVisitor visitor) {
method walk (line 31) | public static void walk(JSONArray array, JSONObjectVisitor visitor) {
method visit (line 35) | protected void visit(JSONObject object) {
method visit (line 50) | protected void visit(JSONArray array) {
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/RoundedViewHelper.java
class RoundedViewHelper (line 28) | public class RoundedViewHelper {
method RoundedViewHelper (line 48) | public RoundedViewHelper(@NonNull Context context, float cornerRadiusD...
method onLayout (line 55) | public void onLayout(boolean changed, int l, int t, int r, int b) {
method preDraw (line 87) | public void preDraw(Canvas canvas) {
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/TransparentStateListDrawable.java
class TransparentStateListDrawable (line 34) | public class TransparentStateListDrawable extends StateListDrawable {
method getOpacity (line 35) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/Version.java
class Version (line 30) | public class Version implements Comparable<Version> {
method Version (line 35) | public Version(int major, int minor, int patch) {
method parse (line 41) | @Nullable
method getPatch (line 61) | public int getPatch() {
method getMinor (line 65) | public int getMinor() {
method getMajor (line 69) | public int getMajor() {
method toString (line 73) | @Override
method equals (line 78) | @Override
method hashCode (line 91) | @Override
method compareTo (line 99) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/view/ActionButton.java
class ActionButton (line 36) | @SuppressLint("ViewConstructor")
type Type (line 38) | public enum Type {
method ActionButton (line 47) | public ActionButton(@NonNull Context context, @NonNull final ActionCon...
method getConfiguration (line 99) | @NonNull
method getType (line 104) | public Type getType() {
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/view/ActionsView.java
class ActionsView (line 39) | @SuppressWarnings("ResourceType")
type Delegate (line 42) | public interface Delegate {
method actionButtonClicked (line 43) | void actionButtonClicked(ActionButton.Type type, @Nullable Uri actio...
method ActionsView (line 57) | public ActionsView(@NonNull Context context, @NonNull AssetManager ass...
method getRoundedCorners (line 144) | private static int getRoundedCorners(@NonNull CardConfiguration config...
method onLayout (line 158) | @Override
method draw (line 164) | @Override
method onClick (line 170) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/view/AssetView.java
class AssetView (line 34) | @SuppressLint("ViewConstructor")
method AssetView (line 38) | public AssetView(@NonNull Context context, @NonNull AssetManager asset...
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/view/BodyView.java
class BodyView (line 37) | @SuppressWarnings("ResourceType")
method BodyView (line 49) | public BodyView(@NonNull Context context, @NonNull AssetManager assetM...
method onLayout (line 98) | @Override
method draw (line 104) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/view/CardView.java
class CardView (line 39) | @SuppressLint("ViewConstructor")
method CardView (line 53) | public CardView(
method sizeForCardSize (line 84) | private static
method onMeasure (line 108) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/view/ContentView.java
class ContentView (line 33) | @SuppressLint("ViewConstructor")
method ContentView (line 37) | public ContentView(@NonNull Context context, @NonNull ContentManager a...
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/view/GifView.java
class GifView (line 35) | @SuppressLint("ViewConstructor")
class DecoderThread (line 37) | private class DecoderThread extends Thread {
method DecoderThread (line 41) | public DecoderThread(@Nullable GifDecoder decoder) {
method run (line 45) | @Override
method GifView (line 96) | public GifView(@NonNull Context context, @Nullable GifDecoder decoder) {
method onAttachedToWindow (line 111) | @Override
method onDetachedFromWindow (line 118) | @Override
method onMeasure (line 131) | @Override
method onDraw (line 168) | @Override
FILE: Android/notifications/src/main/java/com/facebook/notifications/internal/view/HeroView.java
class HeroView (line 37) | @SuppressWarnings("ResourceType")
method HeroView (line 52) | public HeroView(@NonNull Context context, @NonNull AssetManager assetM...
method getRoundedCorners (line 101) | private static int getRoundedCorners(@NonNull CardConfiguration cardCo...
method onMeasure (line 116) | @Override
method onLayout (line 137) | @Override
method draw (line 143) | @Override
FILE: iOS/FBNotifications/FBNotifications/Internal/Models/Actions/FBNCardActionsStyle.h
type FBNCardActionsStyleAttached (line 27) | typedef NS_ENUM(NSUInteger, FBNCardActionsStyle) {
type FBNCardActionsLayoutStyleVertical (line 38) | typedef NS_ENUM(NSUInteger, FBNCardActionsLayoutStyle) {
FILE: iOS/FBNotifications/FBNotifications/Internal/Models/Card/FBNCardSize.h
type FBNCardSizeInvalid (line 22) | typedef NS_ENUM(NSUInteger, FBNCardSize) {
FILE: iOS/FBNotifications/FBNotifications/Internal/Models/Components/FBNCardColor.h
type FBNCardContrastColorWhite (line 26) | typedef NS_ENUM(uint8_t, FBNCardContrastColor) {
Condensed preview — 195 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (676K chars).
[
{
"path": ".gitignore",
"chars": 18,
"preview": "## OS X\n.DS_Store\n"
},
{
"path": ".gitmodules",
"chars": 124,
"preview": "[submodule \"iOS/Vendor/xctoolchain\"]\n\tpath = iOS/Vendor/xctoolchain\n\turl = https://github.com/ParsePlatform/xctoolchain."
},
{
"path": ".travis.yml",
"chars": 1584,
"preview": "branches:\n only:\n - master\nmatrix:\n include:\n - language: objective-c\n os: osx\n osx_image: xcode7.3\n "
},
{
"path": "Android/.gitignore",
"chars": 316,
"preview": "# built application files\n*.apk\n*.ap_\n\n# files for the dex VM\n*.dex\n\n# Java class files\n*.class\n\n# generated files\nbin/\n"
},
{
"path": "Android/build.gradle",
"chars": 321,
"preview": "buildscript {\n repositories {\n jcenter()\n }\n\n dependencies {\n classpath 'com.android.tools.build:"
},
{
"path": "Android/gradle/wrapper/gradle-wrapper.properties",
"chars": 231,
"preview": "#Mon Feb 08 14:52:05 PST 2016\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "Android/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": "Android/gradlew",
"chars": 4971,
"preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n## Gradle start "
},
{
"path": "Android/gradlew.bat",
"chars": 2404,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
},
{
"path": "Android/notifications/build.gradle",
"chars": 4038,
"preview": "apply plugin: 'com.android.library'\n\ngroup = 'com.facebook.android'\n\nbuildscript {\n repositories {\n mavenCentr"
},
{
"path": "Android/notifications/proguard-rules.pro",
"chars": 669,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
},
{
"path": "Android/notifications/src/main/AndroidManifest.xml",
"chars": 377,
"preview": "<manifest package=\"com.facebook.notifications\"\n xmlns:android=\"http://schemas.android.com/apk/res/android\">\n "
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/NotificationCardResult.java",
"chars": 2204,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/NotificationsManager.java",
"chars": 19175,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/activity/CardActivity.java",
"chars": 8174,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/appevents/AppEventsLogger.java",
"chars": 4018,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/asset/Asset.java",
"chars": 1873,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/asset/AssetManager.java",
"chars": 9846,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/asset/ParcelableAssetHandler.java",
"chars": 4770,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/asset/cache/CacheOperation.java",
"chars": 2350,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/asset/cache/ContentCache.java",
"chars": 6314,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/asset/cache/ContentDownloader.java",
"chars": 3890,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/asset/cache/DiskCache.java",
"chars": 2131,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/asset/handlers/BitmapAssetHandler.java",
"chars": 6217,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/asset/handlers/ColorAssetHandler.java",
"chars": 4425,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/asset/handlers/GifAssetHandler.java",
"chars": 6012,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/ActionConfiguration.java",
"chars": 3664,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/ActionsConfiguration.java",
"chars": 6380,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/BodyConfiguration.java",
"chars": 3665,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/CardConfiguration.java",
"chars": 6040,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/configuration/HeroConfiguration.java",
"chars": 4717,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/content/Content.java",
"chars": 2259,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/content/ContentManager.java",
"chars": 2220,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/content/TextContent.java",
"chars": 4951,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/EnumCreator.java",
"chars": 1706,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/FontUtilities.java",
"chars": 1878,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/GifDecoder.java",
"chars": 23515,
"preview": "/**\n * Copyright (c) 2013 Xcellent Creations, Inc.\n *\n * Permission is hereby granted, free of charge, to any person obt"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/InvalidParcelException.java",
"chars": 1648,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/JSONObjectVisitor.java",
"chars": 2166,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/RoundedViewHelper.java",
"chars": 3372,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/TransparentStateListDrawable.java",
"chars": 1837,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/utilities/Version.java",
"chars": 3100,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/view/ActionButton.java",
"chars": 3917,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/view/ActionsView.java",
"chars": 6928,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/view/AssetView.java",
"chars": 2010,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/view/BodyView.java",
"chars": 4653,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/view/CardView.java",
"chars": 6623,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/view/ContentView.java",
"chars": 1866,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/view/GifView.java",
"chars": 6751,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications/src/main/java/com/facebook/notifications/internal/view/HeroView.java",
"chars": 5686,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications-example/build.gradle",
"chars": 931,
"preview": "apply plugin: 'com.android.application'\n\nandroid {\n compileSdkVersion 23\n buildToolsVersion \"23.0.3\"\n\n defaultC"
},
{
"path": "Android/notifications-example/google-services.json",
"chars": 1011,
"preview": "{\n \"project_info\": {\n \"project_id\": \"fbiantestapp\",\n \"project_number\": \"903525066365\",\n \"name\": \"FBIANTestApp\""
},
{
"path": "Android/notifications-example/proguard-rules.pro",
"chars": 669,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
},
{
"path": "Android/notifications-example/src/main/AndroidManifest.xml",
"chars": 2583,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package="
},
{
"path": "Android/notifications-example/src/main/assets/example1.json",
"chars": 1473,
"preview": "{\n \"fb_push_card\": {\n \"version\" : \"1.0\",\n \"dismissColor\": \"#334D5CFF\",\n \"size\": \"small\",\n \"cornerRadius\": 1"
},
{
"path": "Android/notifications-example/src/main/assets/example2.json",
"chars": 1642,
"preview": "{\n \"fb_push_payload\" : {\n \"campaign\" : \"2\"\n },\n \"fb_push_card\": {\n \"version\": \"1.0\",\n \"size\": \"medium\",\n "
},
{
"path": "Android/notifications-example/src/main/assets/example3.json",
"chars": 1592,
"preview": "{\n \"fb_push_payload\" : {\n \"campaign\" : \"3\"\n },\n \"fb_push_card\": {\n \"version\": \"1.0\",\n \"size\": \"medium\",\n "
},
{
"path": "Android/notifications-example/src/main/assets/example4.json",
"chars": 923,
"preview": "{\n \"fb_push_payload\" : {\n \"campaign\" : \"4\"\n },\n \"fb_push_card\": {\n \"version\": \"1.0\",\n \"size\": \"medium\",\n "
},
{
"path": "Android/notifications-example/src/main/assets/example5.json",
"chars": 1209,
"preview": "{\n \"fb_push_payload\" : {\n \"campaign\" : \"5\"\n },\n \"fb_push_card\": {\n \"version\": \"1.0\",\n \"size\": \"large\",\n \""
},
{
"path": "Android/notifications-example/src/main/java/com/facebook/notifications/sample/MainActivity.java",
"chars": 4342,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications-example/src/main/java/com/facebook/notifications/sample/MyGcmListenerService.java",
"chars": 1815,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications-example/src/main/java/com/facebook/notifications/sample/MyInstanceIDListenerService.java",
"chars": 1457,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications-example/src/main/java/com/facebook/notifications/sample/RegistrationIntentService.java",
"chars": 2013,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "Android/notifications-example/src/main/res/layout/activity_main.xml",
"chars": 1085,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.design.widget.CoordinatorLayout\n xmlns:android=\"http://schema"
},
{
"path": "Android/notifications-example/src/main/res/layout/content_main.xml",
"chars": 3788,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n "
},
{
"path": "Android/notifications-example/src/main/res/menu/menu_main.xml",
"chars": 133,
"preview": "<menu\n xmlns:tools=\"http://schemas.android.com/tools\"\n tools:context=\"com.facebook.notifications.sample.MainActivi"
},
{
"path": "Android/notifications-example/src/main/res/values/colors.xml",
"chars": 208,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"colorPrimary\">#3F51B5</color>\n <color name=\"color"
},
{
"path": "Android/notifications-example/src/main/res/values/dimens.xml",
"chars": 253,
"preview": "<resources>\n <!-- Default screen margins, per the Android Design guidelines. -->\n <dimen name=\"activity_horizontal"
},
{
"path": "Android/notifications-example/src/main/res/values/strings.xml",
"chars": 729,
"preview": "<resources>\n <string name=\"app_name\">FBNotificationsSample</string>\n <string name=\"action_settings\">Settings</stri"
},
{
"path": "Android/notifications-example/src/main/res/values/styles.xml",
"chars": 703,
"preview": "<resources>\n\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar"
},
{
"path": "Android/notifications-example/src/main/res/values-v21/styles.xml",
"chars": 355,
"preview": "<resources>>\n <style name=\"AppTheme.NoActionBar\">\n <item name=\"windowActionBar\">false</item>\n "
},
{
"path": "Android/notifications-example/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": "Android/settings.gradle",
"chars": 51,
"preview": "include ':notifications', ':notifications-example'\n"
},
{
"path": "CHANGELOG-Android.md",
"chars": 1865,
"preview": "# Facebook In-App Notifications for Android Changelog\n\n## [1.0.2] - 2016-06-01\n### Added\n- Added automatic logging for p"
},
{
"path": "CHANGELOG-iOS.md",
"chars": 736,
"preview": "# Facebook In-App Notifications for iOS Changelog\n\n## [1.0.1] - 2016-05-10\n\n### Added\n- Added automatic logging of push "
},
{
"path": "CONTRIBUTING.md",
"chars": 1320,
"preview": "# Contributing to Facebook In-App Notifications\nWe want to make contributing to this project as easy and transparent as "
},
{
"path": "FBNotifications.podspec",
"chars": 747,
"preview": "Pod::Spec.new do |s|\n s.name = 'FBNotifications'\n s.version = '1.0.1'\n s.license = { :"
},
{
"path": "Format/Format-1.0.md",
"chars": 5174,
"preview": "# [1.0] Facebook In-App Notifications Format\n\n## Types\n\nWe use basic JSON types to describe everything, since it's porta"
},
{
"path": "Format/Format-Changelog.md",
"chars": 73,
"preview": "# Facebook In-App Notifications Format Changelog\n\n## 1.0\n\nInitial Release"
},
{
"path": "LICENSE",
"chars": 1099,
"preview": "Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n\nFor Facebook In-App Notifications Framework software\n\nY"
},
{
"path": "LICENSE-specification",
"chars": 14650,
"preview": "Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n\nFor In-App Notifications Format specification in Format"
},
{
"path": "README.md",
"chars": 6375,
"preview": "\n\n![Platforms][platforms-svg]\n[![Build Status][build-s"
},
{
"path": "iOS/.gitignore",
"chars": 379,
"preview": "## OS X\n.DS_Store\n\n## Build generated\nbuild/\nDerivedData\n\n## Various settings\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!defa"
},
{
"path": "iOS/FBNotifications/Configurations/FBNotifications-iOS-Dynamic.xcconfig",
"chars": 1515,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/Configurations/FBNotifications-iOS.xcconfig",
"chars": 1482,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/FBNCardViewController.h",
"chars": 3809,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/FBNCardViewController.m",
"chars": 15759,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/FBNConstants.h",
"chars": 1626,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/FBNConstants.m",
"chars": 1252,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/FBNotifications.h",
"chars": 1236,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/FBNotificationsManager.h",
"chars": 6581,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/FBNotificationsManager.m",
"chars": 8051,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/AppEvents/FBNCardAppEventsLogger.h",
"chars": 1549,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/AppEvents/FBNCardAppEventsLogger.m",
"chars": 3077,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Cache/FBNAssetContentCache.h",
"chars": 1563,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Cache/FBNAssetContentCache.m",
"chars": 10110,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Cache/FBNAssetContentCacheOperation.h",
"chars": 1534,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Cache/FBNAssetContentCacheOperation.m",
"chars": 1487,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Color/FBNColorAsset.h",
"chars": 1470,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Color/FBNColorAsset.m",
"chars": 1729,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Color/FBNColorAssetController.h",
"chars": 1282,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Color/FBNColorAssetController.m",
"chars": 2474,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Color/FBNColorAssetViewController.h",
"chars": 1370,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Color/FBNColorAssetViewController.m",
"chars": 2068,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Common/FBNAsset.h",
"chars": 1292,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Common/FBNAssetController.h",
"chars": 1759,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/FBNAssetsController.h",
"chars": 2345,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/FBNAssetsController.m",
"chars": 5641,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/GIF/FBNGIFAsset.h",
"chars": 1460,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/GIF/FBNGIFAsset.m",
"chars": 1654,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/GIF/FBNGIFAssetController.h",
"chars": 1280,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/GIF/FBNGIFAssetController.m",
"chars": 2949,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/GIF/FBNIGIFAssetViewController.h",
"chars": 1364,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/GIF/FBNIGIFAssetViewController.m",
"chars": 2353,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Image/FBNImageAsset.h",
"chars": 1464,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Image/FBNImageAsset.m",
"chars": 1692,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Image/FBNImageAssetController.h",
"chars": 1282,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Image/FBNImageAssetController.m",
"chars": 2948,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Image/FBNImageAssetViewController.h",
"chars": 1370,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Asset/Image/FBNImageAssetViewController.m",
"chars": 2250,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/FBNCardViewController_Internal.h",
"chars": 1744,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Actions/FBNCardActionConfiguration.h",
"chars": 2040,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Actions/FBNCardActionConfiguration.m",
"chars": 2113,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Actions/FBNCardActionsConfiguration.h",
"chars": 2467,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Actions/FBNCardActionsConfiguration.m",
"chars": 3232,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Actions/FBNCardActionsStyle.h",
"chars": 1832,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Actions/FBNCardActionsStyle.m",
"chars": 1807,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Actions/FBNCardButtonAction.h",
"chars": 1346,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Body/FBNCardBodyConfiguration.h",
"chars": 2021,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Body/FBNCardBodyConfiguration.m",
"chars": 2316,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Card/FBNCardConfiguration.h",
"chars": 2063,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Card/FBNCardConfiguration.m",
"chars": 4745,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Card/FBNCardDisplayOptions.h",
"chars": 1714,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Card/FBNCardDisplayOptions.m",
"chars": 1979,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Card/FBNCardSize.h",
"chars": 1485,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Card/FBNCardSize.m",
"chars": 2181,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Components/FBNAnimatedImage.h",
"chars": 1223,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Components/FBNAnimatedImage.m",
"chars": 4164,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Components/FBNCardBackground.h",
"chars": 1276,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Components/FBNCardBackground.m",
"chars": 1621,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Components/FBNCardColor.h",
"chars": 1459,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Components/FBNCardColor.m",
"chars": 2165,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Components/FBNCardFont.h",
"chars": 1514,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Components/FBNCardFont.m",
"chars": 2821,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Components/FBNCardTextAlignment.h",
"chars": 1284,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Components/FBNCardTextAlignment.m",
"chars": 1507,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Components/FBNCardTextContent.h",
"chars": 2213,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Components/FBNCardTextContent.m",
"chars": 3155,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Hero/FBNCardHeroConfiguration.h",
"chars": 2257,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Models/Hero/FBNCardHeroConfiguration.m",
"chars": 2628,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Utilities/FBNCardError.h",
"chars": 1225,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Utilities/FBNCardError.m",
"chars": 1501,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Utilities/FBNCardHash.h",
"chars": 1235,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Utilities/FBNCardHash.m",
"chars": 2348,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Utilities/FBNCardPayload.h",
"chars": 1443,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Utilities/FBNCardPayload.m",
"chars": 3074,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Utilities/FBNCardViewUtilities.h",
"chars": 2530,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Utilities/FBNCardViewUtilities.m",
"chars": 4604,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Utilities/FBNContentSizeProvider.h",
"chars": 334,
"preview": "//\n// FBNContentSizeProvider.h\n// FBNotifications\n//\n// Created by Nikita Lutsenko on 5/20/16.\n// Copyright © 2016 F"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Views/Components/FBNCardDismissButton.h",
"chars": 1219,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Views/Components/FBNCardDismissButton.m",
"chars": 3209,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Views/Components/FBNCardLabel.h",
"chars": 1256,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Views/Components/FBNCardLabel.m",
"chars": 2018,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Views/FBNCardActionButton.h",
"chars": 1652,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Views/FBNCardActionButton.m",
"chars": 2476,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Views/FBNCardActionsViewController.h",
"chars": 1931,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Views/FBNCardActionsViewController.m",
"chars": 8045,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Views/FBNCardBodyViewController.h",
"chars": 1767,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Views/FBNCardBodyViewController.m",
"chars": 4193,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Views/FBNCardHeroViewController.h",
"chars": 1799,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Internal/Views/FBNCardHeroViewController.m",
"chars": 5320,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotifications/FBNotifications/Resources/Info.plist",
"chars": 787,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "iOS/FBNotifications/FBNotifications.xcodeproj/project.pbxproj",
"chars": 79248,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "iOS/FBNotifications/FBNotifications.xcodeproj/xcshareddata/xcschemes/FBNotifications-iOS-Dynamic.xcscheme",
"chars": 2958,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"0730\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "iOS/FBNotifications/FBNotifications.xcodeproj/xcshareddata/xcschemes/FBNotifications-iOS.xcscheme",
"chars": 2934,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"0730\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "iOS/FBNotifications.xcworkspace/contents.xcworkspacedata",
"chars": 285,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"group:FBNotification"
},
{
"path": "iOS/FBNotificationsExample/.gitignore",
"chars": 11,
"preview": "*.framework"
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 585,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"iphone\",\n \"size\" : \"29x29\",\n \"scale\" : \"2x\"\n },\n {\n \"idiom\""
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample/Resources/Base.lproj/LaunchScreen.storyboard",
"chars": 1663,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample/Resources/Base.lproj/Main.storyboard",
"chars": 1588,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample/Resources/Examples/example1.json",
"chars": 1473,
"preview": "{\n \"fb_push_card\": {\n \"version\" : \"1.0\",\n \"dismissColor\": \"#334D5CFF\",\n \"size\": \"small\",\n \"cornerRadius\": 1"
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample/Resources/Examples/example2.json",
"chars": 1642,
"preview": "{\n \"fb_push_payload\" : {\n \"campaign\" : \"2\"\n },\n \"fb_push_card\": {\n \"version\": \"1.0\",\n \"size\": \"medium\",\n "
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample/Resources/Examples/example3.json",
"chars": 1592,
"preview": "{\n \"fb_push_payload\" : {\n \"campaign\" : \"3\"\n },\n \"fb_push_card\": {\n \"version\": \"1.0\",\n \"size\": \"medium\",\n "
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample/Resources/Examples/example4.json",
"chars": 923,
"preview": "{\n \"fb_push_payload\" : {\n \"campaign\" : \"4\"\n },\n \"fb_push_card\": {\n \"version\": \"1.0\",\n \"size\": \"medium\",\n "
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample/Resources/Examples/example5.json",
"chars": 1209,
"preview": "{\n \"fb_push_payload\" : {\n \"campaign\" : \"5\"\n },\n \"fb_push_card\": {\n \"version\": \"1.0\",\n \"size\": \"large\",\n \""
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample/Resources/Info.plist",
"chars": 1293,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample/Source/AppDelegate.h",
"chars": 1236,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample/Source/AppDelegate.m",
"chars": 3886,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample/Source/ViewController.h",
"chars": 1172,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample/Source/ViewController.m",
"chars": 6072,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample/Source/main.m",
"chars": 1301,
"preview": "// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n//\n// You are hereby granted a non-exclusive, worldwi"
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample.xcodeproj/project.pbxproj",
"chars": 19952,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "iOS/FBNotificationsExample/FBNotificationsExample.xcodeproj/xcshareddata/xcschemes/FBNotificationsExample.xcscheme",
"chars": 3483,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"0730\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "iOS/Rakefile",
"chars": 3264,
"preview": "# Copyright (c) 2016-present, Facebook, Inc. All rights reserved.\n#\n# You are hereby granted a non-exclusive, worldwide,"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the facebook/FBNotifications GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 195 files (616.0 KB), approximately 157.0k tokens, and a symbol index with 391 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.