code:
#!/usr/bin/env bash
#
# Copyright (C) 2017,2021 Leah Rowe <info@minifree.org>
# Copyright (C) 2017 Alyssa Rosenzweig <alyssa@rosenzweig.io>
# Copyright (C) 2017 Michael Reed <michael@michaelreed.io>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

[ "x${DEBUG+set}" = 'xset' ] && set -v
set -u -e

# TODO: speed optimization: use ***GIT*** to detect site changes.
#		init .git in www/ but already have a .gitignore (part of
#		untitled) in there, which only allows .md files. that way,
#		git add -A . will always, always only add .md files. then
#		git-whatchanged can be used, always, to just detect changes
#	one thing:
#		it is not possible, using this method, to detect all changes.
#		for example, .include files (and their per-page equivalents)
#		so on some files, modification dates will still be used

# TODO: support MANIFEST.xx for translated news indexes. Individual news pages
#		are still supported, without this change

# TODO: make site change detection more robust. certain things are missed
#		AUDIT NEEDED

# TODO: make untitled work on subdirectories. e.g. https://website.com/~user/

# TODO: gemtext support

# TODO: write an optimized C replacement for pandoc markdown->html conversion
#		pandoc is written is haskell, and C will probably be faster

# TODO: ensure that /tmp is a tmpfs, and recommend this in the documentation

# TODO: in mkhtml, allow auto-nav if no .nav file specified
#		but disable this by default, to maintain old behaviour
#		make it configurable in site.cfg

# TODO: next/previous links on news pages

# TODO: allow per-directory .include overrides, including for translations
#		right now, only global ones and per-page ones are supported

SITECHANGED="n"

# usage: title file
title() {
	firstchar=$(head -c 1 ${1})
	firstthreechars=$(head -c 3 ${1})
	outputstring=""
	if [ "${firstchar}" = "%" ] || [ "${firstchar}" = "#" ]; then
		outputstring="$(sed -n 1p "${1}" | sed -e s-^..--)"
	elif [ "${firstthreechars}" = "---" ]; then
		outputstring="$(sed -n 2p "${1}" | sed -e s-^..--)"
		outputstring="${outputstring#* }"
	else
		outputstring="$(head -n1 "${1}")"
	fi
	printf "%s\n" "${outputstring}"
	return 0
}

# usage: meta file
meta() {
	file="${1}"
	SITEDIR="${2}"
	BLOGDIR="${3}"
	htmlfile=$(printf '%s\n' "${file%.md}.html")

	printf '%s\n' \
	"[$(title "${SITEDIR}/site/${BLOGDIR}${file}")](/${BLOGDIR}${file}){.title}"

	printf '%s\n' \
	"[$(sed -n 3p "${SITEDIR}/site/${BLOGDIR}${file}" | sed -e s-^..--)]{.date}"
	# printf '\n'
	# tail -n +5 "${SITEDIR}/news/${file}" | perl -p0e 's/(\.|\?|\!)( |\n)(.|\n)*/.../g'
	# todo: make this configurable via enable/disable, and make it less cursed

	printf '\n'
	printf '\n'
}

# usage: rss_header
rss_header() {
	BLOGTITLE="${1}"
	DOMAIN="${2}" # without a / at the end, but with http:// or https://
	BLOGDESCRIPTION="${3}"
	BLOGDIR="${4}"

	printf '%s\n' '<rss version="2.0">'
	printf '%s\n' '<channel>'

	printf '%s\n' "<title>${BLOGTITLE}</title>"
	printf '%s\n' "<link>${DOMAIN}${BLOGDIR}</link>"
	printf '%s\n' "<description>$BLOGDESCRIPTION</description>"
}

# usage: rss_main file
rss_main() {
	file="${1}"
	SITEDIR="${2}"
	DOMAIN="${3}"
	BLOGDIR="${4}"

	# render content and escape
	htmlfile="${file%.md}.html"

	PAGETITLE=$(title "${SITEDIR}/site/${BLOGDIR}${file}")
	PAGEURL="${DOMAIN}${BLOGDIR}${htmlfile}"

	printf '%s\n' '<item>'
	printf '%s\n' "<title>${PAGETITLE}</title>"
	printf '%s\n' "<link>${PAGEURL}</link>"
	printf '%s\n' \
	"<description>
Article: ${PAGETITLE}

Web link: ${PAGEURL}
</description>"
	printf '%s\n' '</item>'
}

# usage: rss_footer
rss_footer() {
	printf '%s\n' '</channel>'
	printf '%s\n' '</rss>'
}

# uses the last modified date
# returns 1 if the file has changed
filehasnotchanged() {
	# return 1 means yes, needs to be changed
	# 0 means no, doesn't need to be changed
	f="${1}"

	if [ ! -f "${f}" ]; then
		return 0
	fi

	if [ -d "${f}.date" ]; then
		return 1
	fi

	if [ ! -f "${f}.date" ]; then
		date -r "${f}" +%s > "${f}.date"
		return 1
	fi

	moddate=$(date -r "${f}" +%s)
	if [ "$(cat "${f}.date")" = "${moddate}" ]; then
		return 0
	fi

	printf "%s\n" "${moddate}" > "${f}.date"
	return 1
}

getlangname() {
	FILE="${1%.md}" # path to markdown file
	DEFAULTLANG="${2}"

	REALPAGE="${FILE##*/}"
	PAGELANG="${REALPAGE##*.}"

	if [ "${PAGELANG}" = "${REALPAGE}" ]; then
		PAGELANG="${DEFAULTLANG}"
	fi

	STRINGSCFGPATH="lang/${PAGELANG}/strings.cfg"

	if [ ! -f "${STRINGSCFGPATH}" ]; then
		printf "%s\n" "${PAGELANG}"
		return 0
	fi

	LANGNAME="$(getConfigValue "${STRINGSCFGPATH}" "LANGNAME")"

	if [ "${LANGNAME}" != "" ]; then
		printf "%s\n" "${LANGNAME}"
		return 0
	fi

	printf "%s\n" "${PAGELANG}"
	return 0
}

# convert pandoc-markdown file into html, using a template file
mkhtml() {
	FILE=${1%.md}
	SITEDIR="www/${2}"
	TITLE="${3}"
	CSS="${4}"
	DEFAULTLANG="${5}"

	# will be set to y if page changes are detected
	NEEDCHANGE="n"

	if [ -d "${FILE}.html" ]; then
		printf "%s: %s.html is a DIRECTORY. skipping %s.md\n" \
			"${SITEDIR}" "${FILE}" "${FILE}"
		return 0
	fi

	# e.g. file.md is default(english) and file.ru.md is russian
	REALPAGE="${FILE##*/}"
	PAGELANGUAGE="${REALPAGE##*.}"
	if [ "${PAGELANGUAGE}" = "${REALPAGE}" ]; then
		PAGELANGUAGE="${DEFAULTLANG}"
	else
		FILE="${FILE%.${PAGELANGUAGE}}"
	fi

	STRINGSCFGPATH="lang/${PAGELANGUAGE}/strings.cfg"
	if [ ! -f "${STRINGSCFGPATH}" ]; then
		STRINGSCFGPATH="lang/${DEFAULTLANG}/strings.cfg"
	fi
	if [ ! -f "${STRINGSCFGPATH}" ]; then
		STRINGSCFGPATH="lang/en/strings.cfg"
	fi

	# This is the URI but without any extension, *and without language
	# extension*. This will be used extensively, especially for backlinks
	URI="${FILE##${SITEDIR}/site}" # without file extension e.g. .html

	# Allow a given page to override the default footer
	FOOTERFILE=""
	if [ -f "${FILE}.footer" ]; then
		FOOTERFILE="${FILE}.footer"
	fi
	if [ "${PAGELANGUAGE}" != "${DEFAULTLANG}" ]; then
		if [ -f "${FILE}.${PAGELANGUAGE}.footer" ]; then
			FOOTERFILE="${FILE}.${PAGELANGUAGE}.footer"
		fi
	fi

	# Backlink text for each page. This helps with site navigation
	BACKLINK=""
	if [ "${URI}" != "/index" ]; then # the home page doesn't need a backlink
		if [ "${URI##*/}" = "index" ]; then
			BACKLINK="$(getConfigValue "${STRINGSCFGPATH}" "BACKLINK_PREVDIR")"
		else
			BACKLINK="$(getConfigValue "${STRINGSCFGPATH}" "BACKLINK_CURRENTDIR")"
		fi
	fi

	# Allow a given page to override the default navigation menu
	NAVFILE=""
	if [ -f "${FILE}.nav" ]; then
		NAVFILE="${FILE}.nav"
	fi
	if [ "${PAGELANGUAGE}" != "${DEFAULTLANG}" ]; then
		if [ -f "${FILE}.${PAGELANGUAGE}.nav" ]; then
			NAVFILE="${FILE}.${PAGELANGUAGE}.nav"
		fi
	fi

	# Allow a given page to override the default template
	TEMPLATE=""
	if [ -f "${FILE}.template" ]; then
		TEMPLATE="${FILE}.template"
	fi
	if [ "${PAGELANGUAGE}" != "${DEFAULTLANG}" ]; then
		if [ -f "${FILE}.${PAGELANGUAGE}.template" ]; then
			TEMPLATE="${FILE}.${PAGELANGUAGE}.template"
		fi
	fi

	# Allow a given page to override the default CSS
	CSSOVERRIDE=""
	if [ -f "${FILE}.css" ]; then
		CSSOVERRIDE="--css ${URI}.css"
	fi
	# DO NOT override the main override! add it instead
	if [ "${PAGELANGUAGE}" != "${DEFAULTLANG}" ]; then
		if [ -f "${FILE}.${PAGELANGUAGE}.css" ]; then
			if [ "${CSSOVERRIDE}" != "" ]; then
				CSSOVERRIDE="${CSSOVERRIDE} --css ${URI}.${PAGELANGUAGE}.css"
			else
				CSSOVERRIDE="--css ${URI}.${PAGELANGUAGE}.css"
			fi
		fi
	fi

	if [ "${PAGELANGUAGE}" != "${DEFAULTLANG}" ]; then
		FILE="${FILE}.${PAGELANGUAGE}"
		URI="${URI}.${PAGELANGUAGE}"
	fi

	for f in "${FILE}.md" "${FOOTERFILE}" "${NAVFILE}" "${TEMPLATE}"; do
		if [ "${f}" = "" ]; then continue; fi
		filehasnotchanged "${f}" || NEEDCHANGE="y"
	done

	if [ "${NEEDCHANGE}" = "n" ] && [ -f "${FILE}.html" ]; then
		return 0
	fi

	if [ "${FOOTERFILE}" = "" ]; then
		if [ -f "${SITEDIR}/site/footer.include" ]; then
			FOOTERFILE="${SITEDIR}/site/footer.include"
		fi
		if [ "${PAGELANGUAGE}" != "${DEFAULTLANG}" ]; then
			if [ -f "${SITEDIR}/site/footer.${PAGELANGUAGE}.include" ]; then
				FOOTERFILE="${SITEDIR}/site/footer.${PAGELANGUAGE}.include"
			fi
		fi
	fi

	if [ "${NAVFILE}" = "" ]; then
		if [ -f "${SITEDIR}/site/nav.include" ]; then
			NAVFILE="${SITEDIR}/site/nav.include"
		fi
		if [ "${PAGELANGUAGE}" != "${DEFAULTLANG}" ]; then
			if [ -f "${SITEDIR}/site/nav.${PAGELANGUAGE}.include" ]; then
				NAVFILE="${SITEDIR}/site/nav.${PAGELANGUAGE}.include"
			fi
		fi
	fi

	if [ "${TEMPLATE}" = "" ]; then
		if [ -f "${SITEDIR}/site/template.include" ]; then
			TEMPLATE="${SITEDIR}/site/template.include"
		fi
		if [ "${PAGELANGUAGE}" != "${DEFAULTLANG}" ]; then
			if [ -f "${SITEDIR}/site/template.${PAGELANGUAGE}.include" ]; then
				TEMPLATE="${SITEDIR}/site/template.${PAGELANGUAGE}.include"
			fi
		fi
	fi

	# The news and sitemap function need to know this
	# because they will only be called if this variable is set to y
	SITECHANGED="y"

	TMPFILE=$(mktemp -t untitled_www.XXXXXXXXXX)

	date=""
	author=""
#	title="Webpage" # dirty hack

	firstchar=$(head -c 1 ${1})
	firstthreechars=$(head -c 3 ${1})
	if [ "${firstchar}" = "#" ]; then
		filetitle="$(title "${1}")"
		TMPFILE2=$(mktemp -t untitled_www.XXXXXXXXXX)
		head -n1 title.example > "${TMPFILE2}"
		printf "title: %s\n" "${filetitle}" >> "${TMPFILE2}"
		tail -n-2 title.example	>> "${TMPFILE2}"
		title="$(cat "${TMPFILE2}")"
		rm -f "${TMPFILE2}"
		pagetext="$(tail -n+2 "${1}")"
	elif [ "${firstchar}" = "%" ]; then
		TMPFILE2=$(mktemp -t untitled_www.XXXXXXXXXX)

		title="$(head -n3 ${1})"
		printf "%s\n" "${title}" > $TMPFILE2
		chunk="$(head -n1 $TMPFILE2)"

		chunk="$(tail -n+2 $TMPFILE2)"
		printf "%s\n" "${chunk}" > $TMPFILE2
		author="$(head -n1 $TMPFILE2)"

		chunk="$(tail -n+2 $TMPFILE2)"
		printf "%s\n" "${chunk}" > $TMPFILE2
		date="$(head -n1 $TMPFILE2)"

		rm -f "$TMPFILE2"

		pagetext="$(tail -n+4 ${1})"
	elif [ "${firstthreechars}" = "---" ]; then
		title="$(head -n4 ${1})"
		author=""
		date=""
		pagetext="$(tail -n+5 ${1})"
	else
		printf "WARNING: no title detected. defaulting to first line as title\n"
		filetitle="$(title "${1}")"
		TMPFILE2=$(mktemp -t untitled_www.XXXXXXXXXX)
		head -n1 title.example > "${TMPFILE2}"
		printf "title: %s\n" "${filetitle}" >> "${TMPFILE2}"
		tail -n-2 title.example	>> "${TMPFILE2}"
		title="$(cat "${TMPFILE2}")"
		rm -f "${TMPFILE2}"

		pagetext="$(tail -n+2 "${1}")"
	fi

	printf "%s\n" "${title}" > "$TMPFILE"

	if [ "${NAVFILE}" != "" ]; then
		NAVFILECONTENTS="$(cat "${NAVFILE}")"
		printf "\n
\n%s\n
\n" \
			"${NAVFILECONTENTS}" >> "${TMPFILE}"
	fi

	# insert the language select menu
	LANGMENU=""
	DEFAULTPAGE="${FILE##*/}"
	DEFAULTPAGE="${DEFAULTPAGE%.*}"
	if [ "${DEFAULTPAGE}" != "" ]; then
		for page in "${FILE%/*}/${DEFAULTPAGE}".*.md; do
			if [ ! -f "${page}" ]; then continue; fi
			LANGNAME=$(getlangname "${page}" "${DEFAULTLANG}")
			LANGMENU="${LANGMENU} | [${LANGNAME}](${page##*/})"
		done
	fi
	if [ "${LANGMENU}" != "" ]; then
		LANGMENU="${LANGMENU# | }"
		if [ -f "${FILE%/*}/${DEFAULTPAGE}.md" ]; then
			LANGNAME=$(getlangname "${FILE%/*}/${DEFAULTPAGE}.md" "${DEFAULTLANG}")
			if [ "${DEFAULTPAGE}" = "index" ]; then
				LANGMENU="[${LANGNAME}](./) | ${LANGMENU}"
			else
				LANGMENU="[${LANGNAME}](${DEFAULTPAGE}.md) | ${LANGMENU}"
			fi
		fi
	fi
	if [ "${LANGMENU}" != "" ]; then
		# TODO: display "select language" text
		printf "\n%s\n" "${LANGMENU}" >> "${TMPFILE}"
	fi

	if [ "${BACKLINK}" != "" ]; then
		printf "\n%s\n" "${BACKLINK}" >> "${TMPFILE}"
	fi

	# the directory was already checked. no need to
	# check the validity of it, because it's part
	# of untitled, not part of a given site, and
	# what goes in untitled is reviewed thoroughly

	if [ "${author}" != "" ] && [ "${date}" != "" ]; then
		PUBLISHED_BY="$(getConfigValue "${STRINGSCFGPATH}" "PUBLISHED_BY")"
		printf "\n%s %s\n" "${PUBLISHED_BY}" "${author#% }" >> "${TMPFILE}"

		PUBLICATION_DATE="$(getConfigValue "${STRINGSCFGPATH}" "PUBLICATION_DATE")"
		printf "\n%s %s\n" "${PUBLICATION_DATE}" "${date#% }" >> "${TMPFILE}"
	fi

	printf "\n%s\n" "${pagetext}" >> "$TMPFILE"

	if [ "${FOOTERFILE}" != "" ]; then
		FOOTERFILECONTENTS="$(cat "${FOOTERFILE}")"
		printf "\n
\n%s\n
\n" \
			"${FOOTERFILECONTENTS}" >> "${TMPFILE}"
	fi

	MARKDOWN_LINK="$(getConfigValue "${STRINGSCFGPATH}" "MARKDOWN_LINK")"
	printf "\n%s <%s%s.md>\n" "${MARKDOWN_LINK}" "${DOMAIN}" \
		"${URI##/}" >> "${TMPFILE}"

	RSS_LINK="$(getConfigValue "${STRINGSCFGPATH}" "RSS_LINK")"
	if [ -f "${SITEDIR}/site/feed.xml" ]; then
		printf "\n%s\n" "${RSS_LINK}" >> "${TMPFILE}"
	fi

	SITEMAP_LINK="$(getConfigValue "${STRINGSCFGPATH}" "SITEMAP_LINK")"
	if [ -f "${SITEDIR}/site/sitemap.md" ]; then
		printf "\n%s\n" "${SITEMAP_LINK}" >> "${TMPFILE}"
	fi

	# TODO: make this configurable to enable/disable in site.cfg
	SHAMELESS_PLUG="$(getConfigValue "${STRINGSCFGPATH}" "SHAMELESS_PLUG")"
	printf "\n%s\n" "${SHAMELESS_PLUG}" >> "${TMPFILE}"

	# change out .md -> .html
	# but not for external links
	sed -i -e \
		'/\/\//!{s/\.md\(#[a-zA-Z0-9_-]*\)\?\([])]*\)/.html\1\2/g}' \
		"$TMPFILE"

	# TOC is always enabled on news pages
	TOC=$(grep -q "^x-toc-enable: true$" "$TMPFILE" && \
		printf '%s\n' "--toc --toc-depth=4") || TOC=""

	# TODO: make toc depth configurable in site.cfg
	if [ -f "${FILE%/*}/MANIFEST" ]; then
		TOC="--toc --toc-depth=4"
	fi

	# work around heterogenous pandoc versions
	SMART=$(pandoc -v | grep -q '2\.0' || \
		printf '%s\n' "-f markdown+smart -t html") || SMART=""

	PDIR="$(getConfigValue "${STRINGSCFGPATH}" "PDIR")"

	printf "Generating '%s.html'\n" "${FILE}"

	HTMLLINK="${MARKDOWN_LINK%.md}"

	HTMLLINK="${DOMAIN}${URI##/}"

	if [ "${HTMLLINK##*/}" = "index" ]; then
		HTMLLINK="${HTMLLINK%/*}/"
	else
		HTMLLINK="${HTMLLINK}.html"
	fi

	# chuck through pandoc
	#
	# $CSS must not be quoted, otherwise pandoc interprets '--css /headercenter.css'
	# as one argument, when it is actually two.
	# Same for $TITLE
	pandoc -V lang=${PAGELANGUAGE} -V dir=${PDIR} $TOC $SMART "$TMPFILE" \
		--preserve-tabs --tab-stop 8 \
		-V antisocialurl="${HTMLLINK}" -s ${CSS} ${CSSOVERRIDE} ${TITLE} \
		--template ${TEMPLATE} --metadata return="" > "$FILE.html"

	# generate section title anchors as [link]
	sed -i \
		-e 's_^<h\([123]\) id="\(.*\)">\(.*\)</h\1>_
<h\1 id="\2">\3</h\1>[link]
_' \
		"$FILE.html"

	# clean up temporary file
	rm -f "$TMPFILE"
}

getConfigValue() {
	CONFIGFILE="${1}" # e.g. www/foo/site.cfg
	ITEM="${2}" # e.g. TITLE
	TMPCFG=$(mktemp -t untitled_www.XXXXXXXXXX)
	grep "${ITEM}" "${CONFIGFILE}" > "${TMPCFG}"
	VALUE="$(head -n1 $TMPCFG)"
	VALUE="${VALUE##*$ITEM=\"}"
	VALUE="${VALUE%\"*}"
	printf "%s\n" "${VALUE}"
	rm -f "${TMPCFG}"
}

# ensure that it's just a file name, no directories specified
# if the path includes directories, it just means that this will be mangled.
# code that uses this function is: mostly the mknews function
# should be safe
sanitizefilename() {
	page="${1##*/}"
	page="${page%/}" # avoid refering to directory
	page="$(printf "%s\n" "${1}" | sed -e 's/\ //g')"
	page="$(printf "%s\n" "${1}" | sed -e 's/\.\.//g')"
	printf "${page}\n"
}

mknews() {
	SITEDIR="${1}"
	SITENAME="${2}"
	BLOGDIR="${3}"
	DOMAIN="${4}"
	TITLE="${5}"
	CSS="${6}"
	DEFAULTLANG="${7}"

	#if [ -f "${SITEDIR}/site/${BLOGDIR}MANIFEST" ]; then

	for MANIFEST in $(find -L "${SITEDIR}/site/" -type f -name "MANIFEST"); do

		if [ ! -f "${MANIFEST}" ]; then continue; fi

		BLOGTITLE="News"
		BLOGDESCRIPTION="News"
		MANIFESTDIR="${MANIFEST##${SITEDIR}/site/}"
		MANIFESTDIR="${MANIFESTDIR%MANIFEST}"
		if [ -f "${SITEDIR}/site/${MANIFESTDIR}news.cfg" ]; then
			BLOGTITLE="$(getConfigValue "${SITEDIR}/site/${MANIFESTDIR}news.cfg" "BLOGTITLE")"
			BLOGDESCRIPTION="$(getConfigValue "${SITEDIR}/site/${MANIFESTDIR}news.cfg" "BLOGDESCRIPTION")"

			if [ "${BLOGTITLE}" = "" ]; then BLOGTITLE="News"; fi
			if [ "${BLOGDESCRIPTION}" = "" ]; then BLOGDESCRIPTION="News"; fi
		fi

		# Dermine the order of news articles in news/index.md and news/feed.xml
		FILES="$(cat "${MANIFEST}")"
		TMPFILE=$(mktemp -t untitled_www.XXXXXXXXXX)
		printf "\n" > "${TMPFILE}"
		for f in ${FILES}; do
			page="$(sanitizefilename "${f}")"
			if [ ! -f "${SITEDIR}/site/${MANIFESTDIR}${page}" ]; then
				printf "build-html: news file '%s' does not exist in site '%s' for news section '%s'. Skipping this news section\n" \
					"${page}" "${SITENAME}" "${MANIFESTDIR}" \
					>> "${TMPFILE}"
			fi
			if [ -L "${SITEDIR}/site/${MANIFESTDIR}${page}" ]; then
				printf "build-html: news file '%s' is a symlink in news section '%s' for site '%s'. Skipping this news section.\n" \
					"${page}" "${MANIFESTDIR}" "${SITENAME}" \
					>> "${TMPFILE}"
			fi
			if [ "$(readlink -f "$(pwd)/${SITEDIR}/site/${MANIFESTDIR}${page}" 2>&1)" \
					!= "$(pwd)/${SITEDIR}/site/${MANIFESTDIR}${page}" ]; then

				printf "build-html: news file '%s' different when running readlink -f. skipping this news section\n" \
					"${page}" \
					>> "${TMPFILE}"

			fi
		done
		if [ "$(cat "${TMPFILE}")" != "" ]; then
			cat "${TMPFILE}"
			rm -f "${TMPFILE}"
			continue
		fi
		rm -f "${TMPFILE}"

		# generate the index file
		if [ -f "${SITEDIR}/site/${MANIFESTDIR}news-list.md.include" ]; then
			cat "${SITEDIR}/site/${MANIFESTDIR}news-list.md.include" \
				> "${SITEDIR}/site/${MANIFESTDIR}index.md"
		else
			printf "MANIFEST present for %s in %s but no news-list.md.include present with that MANIFEST. Skipping this news section.\n" \
				"${SITEDIR}" "${MANIFESTDIR}"
			continue
		fi
		if [ "${MANIFESTDIR%/}" = "${BLOGDIR%/}" ]; then
			printf "\nSubscribe to RSS: [/feed.xml](/feed.xml)\n\n" \
				>> "${SITEDIR}/site/${MANIFESTDIR}index.md"
		else
			printf "\nSubscribe to RSS: [/%sfeed.xml](/%sfeed.xml)\n\n" \
				"${MANIFESTDIR}" "${MANIFESTDIR}" \
				>> "${SITEDIR}/site/${MANIFESTDIR}index.md"
		fi
		for f in $FILES
		do
			page="$(sanitizefilename "${f}")"
			meta "${page}" "${SITEDIR}" "${MANIFESTDIR}" \
				>> "${SITEDIR}/site/${MANIFESTDIR}index.md"
		done

		# generate the RSS index
		rss_header "$BLOGTITLE" "$DOMAIN" "$BLOGDESCRIPTION" \
			"${MANIFESTDIR}" > "${SITEDIR}/site/${MANIFESTDIR}feed.xml"
		for f in $FILES
		do
			page="$(sanitizefilename "${f}")"
			rss_main "$page" "${SITEDIR}" "${DOMAIN}" "${MANIFESTDIR}" \
				>> "${SITEDIR}/site/${MANIFESTDIR}feed.xml"
		done
		rss_footer >> "${SITEDIR}/site/${MANIFESTDIR}feed.xml"

		if [ "${SITEDIR}/site/${MANIFESTDIR}feed.xml" \
				!= "${SITEDIR}/site/feed.xml" ] \
				&& [ "${MANIFESTDIR}" = "${BLOGDIR}" ]; then

			rm -f "${SITEDIR}/site/feed.xml"

			cp "${SITEDIR}/site/${MANIFESTDIR}feed.xml" \
				"${SITEDIR}/site/feed.xml"
		fi

		if [ -f "${SITEDIR}/site/${MANIFESTDIR}index.md" ]; then
			mkhtml "${SITEDIR}/site/${MANIFESTDIR}index.md" \
				"${SITEDIR##*/}" "${TITLE}" "${CSS}" \
				"${DEFAULTLANG}"
		fi

	done
	#fi
}

mksitemap() {
	SITEDIR="${1}"
	TITLE="${2}"
	CSS="${3}"
	DEFAULTLANG="${4}"
	if [ ! -f "${SITEDIR}/site/sitemap.include" ]; then return 0; fi
	# only generate a sitemap if one is specified

	TMPFILE=$(mktemp -t untitled_www.XXXXXXXXXX)

	cat "${SITEDIR}/site/sitemap.include" > "${TMPFILE}"
	printf "\n\n" >> "${TMPFILE}"

	printf "
\n" >> "${TMPFILE}"
	for MDFILE in $(find -L "${SITEDIR}/site/" -type f -name "*.md"); do
		if [ ! -f "${MDFILE}" ]; then continue; fi
		if [ "${MDFILE}" = "${SITEDIR}/site/sitemap.md" ]; then continue; fi
		URI="${MDFILE##${SITEDIR}/site}"
		URI="${URI%index*}"
		printf "* %s: [%s](%s)\n" "${URI}" "$(title "${MDFILE}")" \
			"${URI}" \
			>> "${TMPFILE}"
	done
	printf "
\n\n" >> "${TMPFILE}"

	cp "${TMPFILE}" "${SITEDIR}/site/sitemap.md"
	rm -f "${TMPFILE}"

	if [ -f "${SITEDIR}/site/sitemap.md" ]; then
		mkhtml "${SITEDIR}/site/sitemap.md" \
			"${SITEDIR##*/}" "${TITLE}" "${CSS}" "${DEFAULTLANG}"
	fi
}

buildsite() {
	SITECHANGED="n"
	SITEDIR="${1}"

	printf "\nProcessing files in site: %s\n" "${SITEDIR##*/}"

	if [ ! -d "${SITEDIR}" ]; then return 0; fi
	if [ -f "${SITEDIR}" ]; then return 0; fi

	SITENAME="${SITEDIR}"

	# check if there are symlinks in the site directory
	if [ -L "${SITEDIR}" ]; then
		printf "%s site dir is a symlink. skipping this site\n" \
			"${SITEDIR}"
		return 0
	fi

	TMPFILE=$(mktemp -t untitled.XXXXXXXXXX)
	printf "\n" > "${TMPFILE}"
	for i in $(find -L "${SITEDIR}" ! -path "${SITEDIR}/.git"); do
		if [ -L "${i}" ]; then
			printf "%s\n" "${i}" 2>&1 >> "${TMPFILE}"
		fi
	done
	if [ "$(cat "${TMPFILE}")" != "" ]; then
		printf "ERROR: symbolic links present in the %s directory. skipping this site. List:\n" \
			"${SITEDIR}"
		cat "${TMPFILE}" | sort > "${TMPFILE}"
		rm -f "${TMPFILE}"
		return 0
	fi
	rm -f "${TMPFILE}"

	TEMPLATE="${SITEDIR}/site/template.include"

	# Check whether the required files are present.
	# ALSO: Clean the site if site-wide changes are detected
	# For example: if a template changed, purge the whole site and re-build
	FILELIST=$(mktemp -t untitled_www.XXXXXXXXXX)
	printf "%s\n" "${TEMPLATE}" > "${FILELIST}"
	printf "%s/site.cfg\n" "${SITEDIR}" >> "${FILELIST}"
	for f in $(cat "${FILELIST}"); do
		if [ ! -f "${f}" ]; then
			printf "%s missing for site '%s'. Skipping this site\n" \
				"${f}" "${SITEDIR}"
			rm -f "${FILELIST}"
			return 0
		fi
	done
	for f in "${SITEDIR}"/site/footer*.include "${SITEDIR}"/site/nav*.include; do
		if [ -f "${f}" ]; then
			printf "%s\n" "${f}" >> "${FILELIST}"
		fi
	done
	for f in $(cat "${FILELIST}"); do
		filehasnotchanged "${f}" || SITECHANGED="y"
	done
	rm -f "${FILELIST}"

	if [ "${SITECHANGED}" = "y" ]; then
		printf "Site-wide changes detected in '%s'. Cleaning now.\n" \
			"${SITEDIR}"
		./clean "${SITENAME}"
	fi
	SITECHANGED="n"

	# TODO: allow per-page override of site.cfg

	TITLE="$(getConfigValue "${SITEDIR}/site.cfg" "TITLE")"
	if [ "${TITLE}" = "" ]; then TITLE="-T untitled_output"; fi

	CSS="$(getConfigValue "${SITEDIR}/site.cfg" "CSS")"
	# optional. if empty, there just won't be any css

	DOMAIN="$(getConfigValue "${SITEDIR}/site.cfg" "DOMAIN")"
	if [ "${DOMAIN}" = "" ]; then
		printf "%s/site.cfg does not specify DOMAIN. Exiting\n" "${SITEDIR}"
		return 1
	fi

	BLOGTITLE="$(getConfigValue "${SITEDIR}/site.cfg" "BLOGTITLE")"
	if [ "${BLOGTITLE}" = "" ]; then BLOGTITLE="News"; fi

	BLOGDESCRIPTION="$(getConfigValue "${SITEDIR}/site.cfg" "BLOGDESCRIPTION")"
	if [ "${BLOGDESCRIPTION}" = "" ]; then BLOGDESCRIPTION="News"; fi

	DEFAULTLANG="$(getConfigValue "${SITEDIR}/site.cfg" "DEFAULTLANG")"
	DEFAULTLANG="${DEFAULTLANG##*/}"
	if [ "${DEFAULTLANG}" = "" ]; then DEFAULTLANG="en"; fi
	if [ ! -d "lang/${DEFAULTLANG}" ]; then DEFAULTLANG="en"; fi

	BLOGDIR="$(getConfigValue "${SITEDIR}/site.cfg" "BLOGDIR")"
	# optional. if not set, this will be empty, and blog will be homepage
	TESTDIR="$(pwd)/${SITEDIR}/site/${BLOGDIR%/}"
	if [ "$(readlink -f "${TESTDIR}" 2>&1)" != "${TESTDIR%/}" ]; then
		printf "Invalid BLOGDIR in site.cfg for %s. skipping this site\n" "${SITEDIR}"
		return 0
	fi
	if [ -L "${TESTDIR}" ]; then
		printf "BLOGDIR in site.cfg for %s is a symlink. skipping the site\n" "${SITEDIR}"
		return 0
	fi
	if [ -f "${TESTDIR}" ]; then
		printf "BLOGDIR in site.cfg for %s is a file. skipping this site\n" "${SITEDIR}"
		return 0
	fi
	TESTDIR="${TESTDIR##$(pwd)/${SITEDIR}/site/}"

	for mdfile in $(find -L ${SITEDIR}/site/ -name '*.md'); do
		mkhtml "${mdfile}" "${SITEDIR##*/}" "${TITLE}" "${CSS}" \
			"${DEFAULTLANG}"
	done

	if [ "${SITECHANGED}" = "y" ]; then
		mksitemap "${SITEDIR}" "${TITLE}" "${CSS}" "${DEFAULTLANG}"
		if [ ! -d "${SITEDIR}/site/${TESTDIR}" ]; then
			printf "BLOGDIR does not exist in %s. not building news sections for this site\n" "${SITEDIR}"
		else
			mknews "${SITEDIR}" "${SITENAME}" "${BLOGDIR}" "${DOMAIN}" "${TITLE}" "${CSS}" "${DEFAULTLANG}"
		fi
	else
		printf "this website has not been changed. skipping.\n"
	fi
}

buildpage() {
	[[ $# -lt 1 ]] && return 0

	MDFILE="${1}"
	MDFILE="${MDFILE#/}"
	MDFILE="${MDFILE#./}"
	MDFILELOCAL="${MDFILE##*/}"

	[[ "${MDFILE%.md}" = "${MDFILE}" ]] && return 0

	# path translation
	# e.g. libreboot/index.md becomes www/libreboot/site/index.md
	# e.g. libreboot/site/index.md becomes www/libreboot/site/index.md
	# e.g. www/libreboot/index.md becomes www/libreboot/site/index.md
	# in all cases, the correct path is: www/libreboot/site/index.md
	MDFILE="${MDFILE#www/}"
	SITESITE="${MDFILE#*/}"
	[[ "${SITESITE#site/}" = "${SITESITE}" ]] && SITESITE="site/${SITESITE}"
	MDFILE="www/${MDFILE%%/*}/${SITESITE}"

	[[ ! -f "${MDFILE}" ]] && return 0
	[[ -L "${MDFILE}" ]] && return 0

	# Do not allow if the file is contained within a symlinked directory
	absolutepath="$(pwd)/${MDFILE}"
	absolutepath_readlink="$(readlink -f "${absolutepath}" 2>&1)"
	if [ "${absolutepath_readlink}" != "${absolutepath}" ]; then
		return 0
	fi

	SITEDIR="${MDFILE#www/}"
	[[ "${SITEDIR}" = "${MDFILE}" ]] && return 0
	SITENAME="${SITEDIR%%/*}"
	[[ "${SITENAME}" = "${SITEDIR}" ]] && return 0
	SITEDIR="www/${SITENAME}"
	[[ ! -d "${SITEDIR}" ]] && return 0

	printf "\nPage: %s\n" "${MDFILE}"
	printf "Processing page in site directory: %s/site/\n" "${SITEDIR}"

	if [ ! -f "${SITEDIR}/site.cfg" ]; then
		printf "%s missing for site '%s'. Skipping this page\n" \
			"${f}" "${SITEDIR}"
		return 0
	fi

	DOMAIN="$(getConfigValue "${SITEDIR}/site.cfg" "DOMAIN")"
	# it doesn't matter if this is empty

	TITLE="$(getConfigValue "${SITEDIR}/site.cfg" "TITLE")"
	if [ "${TITLE}" = "" ]; then TITLE="-T untitled_output"; fi

	CSS="$(getConfigValue "${SITEDIR}/site.cfg" "CSS")"
	# optional. if empty, there just won't be any css

	DEFAULTLANG="$(getConfigValue "${SITEDIR}/site.cfg" "DEFAULTLANG")"
	DEFAULTLANG="${DEFAULTLANG##*/}"
	if [ "${DEFAULTLANG}" = "" ]; then DEFAULTLANG="en"; fi
	if [ ! -d "lang/${DEFAULTLANG}" ]; then DEFAULTLANG="en"; fi

	mkhtml "${MDFILE}" "${SITEDIR##*/}" "${TITLE}" "${CSS}" \
		"${DEFAULTLANG}"
}

if [ -f "www" ]; then
	printf "untitled error: www is a file. should be a directory. exiting.\n"
	exit 1
fi
if [ ! -d "www" ]; then
	printf "untitled error: no www/ directory. Exiting\n"
	exit 1
fi
if [ -L "www" ]; then
	printf "untitled error: www/ directory is a symlink. Exiting\n"
	exit 1
fi

mode=""

if [ $# -lt 1 ]; then
	mode="sites"
else
	mode="${1}"
	shift 1
fi

if [ "${mode}" = "pages" ] && [ $# -lt 1 ]; then
	mode="sites"
fi

if [ "${mode}" = "sites" ]; then
	if [ $# -gt 0 ]; then
		for sitename in "${@}"; do
			startdate=$(date +%s%N | cut -b1-13)
			buildsite "www/${sitename##*/}"
			enddate=$(date +%s%N | cut -b1-13)
			endtime=$(( enddate - startdate ))
			printf "time: %d milliseconds\n" "${endtime}"
		done
	else
		for sitedir in www/*; do
			startdate=$(date +%s%N | cut -b1-13)
			buildsite "${sitedir}"
			enddate=$(date +%s%N | cut -b1-13)
			endtime=$(( enddate - startdate ))
			printf "time: %d milliseconds\n" "${endtime}"
		done
	fi
elif [ "${mode}" = "pages" ]; then
	for pagepath in "$@"; do
		startdate=$(date +%s%N | cut -b1-13)

		buildpage "${pagepath}"

		enddate=$(date +%s%N | cut -b1-13)
		endtime=$(( enddate - startdate ))
		printf "time: %d milliseconds\n" "${endtime}"
	done
else
	printf "error: unrecognized command: %s\n\n" "${mode}"
	printf "available commands:\n\tsites\n\tpages\n\n"
	printf "Consult the documentation to understand these commands.\n"
	exit 1
fi

path: build


code:
#!/usr/bin/env bash
#
# Copyright (C) 2021 Leah Rowe <info@minifree.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

[ "x${DEBUG+set}" = 'xset' ] && set -v
set -e

if [ ! -d "www" ]; then
	printf "untitled error: no www/ directory. Exiting\n"
	exit 1
fi

# NOTE: the cleansite script does check for symlinks, but unlike in the build
# script, an entire site won't simply be skipped for having symlinks.

cleansite() {
	SITENAME="${1}"

	if [ ! -d "www/${SITENAME}/site" ]; then
		printf "Site '%s' has no site directory; skipping clean\n" "${SITENAME}"
		return 0
	fi
	if [ -L "www/${SITENAME}" ] || [ -L "www/${SITENAME}/site" ]; then
		printf "Site '%s' is a symlink directory. skipping clean\n" "${SITENAME}"
		return 0
	fi

	printf "Cleaning generated files from site: '%s'\n" "${SITENAME}"

	for f in $(find -L www/"${SITENAME}"/site/ -type f -name '*.html'); do
		if [ ! -f "${f}" ]; then continue; fi
		if [ -L "${f}" ]; then continue; fi
		if [ -f "${f%.html}.md" ] && [ ! -L "${f%.html}.md" ]; then
			rm -f "${f}"
		fi
	done

	for f in $(find -L "www/${SITENAME}/site" -type f -name "*.date"); do
		if [ ! -f "${f}" ]; then continue; fi
		if [ -L "${f}" ]; then continue; fi
		if [ "${f##*/}" = "footer.include.date" ] \
			|| [ "${f##*/}" = "nav.include.date" ] \
			|| [ "${f##*/}" = "template.include.date" ]; then

			continue
		fi
		rm -f "${f}"
	done

	rm -f www/"${SITENAME}"/site/feed.xml
	for f in $(find -L www/"${SITENAME}"/site/ -type f -name 'MANIFEST'); do
		if [ ! -f "${f}" ]; then continue; fi
		if [ -L "${f}" ]; then continue; fi
		for ff in "${f%MANIFEST}"{index.md,index.html,feed.xml}; do
			if [ ! -f "${ff}" ]; then continue; fi
			if [ -L "${ff}" ]; then continue; fi

			rm -f "${ff}"
		done
	done
}

if [ $# -gt 0 ]; then
	for site in "${@}"; do
		sitename="${site##*/}"
		cleansite "${sitename}"
	done
else
	for sitedir in www/*; do
		cleansite "${sitedir##*/}"
	done
fi

repo_name: Ghosta/ghosta

path: clean

license: mit


code:
#!/bin/bash
security find-certificate -c "Apple Worldwide Developer Relations Certification Authority" -a -Z | awk '/SHA-1/{system("security delete-certificate -Z "$NF)}'
TEMP="$(mktemp -d -t com.DanTheMan827.WWDR-Fix)"
curl "https://developer.apple.com/certificationauthority/AppleWWDRCA.cer" > "$TEMP/AppleWWDRCA.cer"
security add-certificates "$TEMP/AppleWWDRCA.cer"
rm "$TEMP/AppleWWDRCA.cer"


code:
LANGNAME="English"
LANGSELSTR="Select language:"
BACKLINK_PREVDIR="[Return to previous index](../)"
BACKLINK_CURRENTDIR="[Return to index](./)"
PUBLISHED_BY="Article published by:"
PUBLICATION_DATE="Date of publication:"
MARKDOWN_LINK="Markdown file for this page:"
RSS_LINK="[Subscribe to RSS for this site](/feed.xml)"
SITEMAP_LINK="[Site map](/sitemap.md)"
SHAMELESS_PLUG="This HTML page was generated by the [untitled static site generator](https://untitled.vimuser.org/)."
PDIR="ltr"


code:
LANGNAME="Français"
LANGSELSTR="Sélectionner le language"
BACKLINK_PREVDIR="[Return to previous index](../)"
BACKLINK_CURRENTDIR="[Return to index](./)"
PUBLISHED_BY="Article publié par:"
PUBLICATION_DATE="Article publié le:"
MARKDOWN_LINK="Fichier Markdown pour cette page:"
RSS_LINK="[S'abonner à RSS](/feed.xml)"
SITEMAP_LINK="[Plan du site](/sitemap.md)"
SHAMELESS_PLUG="Cette page HTML a été générée par le *[untitled static site generator](https://untitled.vimuser.org/)*."
PDIR="ltr"


code:
LANGNAME="polski"
LANGSELSTR="Select language:"
BACKLINK_PREVDIR="[Powróć do poprzedniej strony](../)"
BACKLINK_CURRENTDIR="[Powróć do poprzedniej strony](./)"
PUBLISHED_BY="Article published by:"
PUBLICATION_DATE="Date of publication:"
MARKDOWN_LINK="Markdown file for this page:"
RSS_LINK="[Subscribe to RSS for this site](/feed.xml)"
SITEMAP_LINK="[Site map](/sitemap.md)"
SHAMELESS_PLUG="This HTML page was generated by the [untitled static site generator](https://untitled.vimuser.org/)."
PDIR="ltr"


code:
LANGNAME="Português"
LANGSELSTR="Select language:"
BACKLINK_PREVDIR="[Voltar à página anterior](../)"
BACKLINK_CURRENTDIR="[Voultar ao indice](./)"
PUBLISHED_BY="Artigo publicado por:"
PUBLICATION_DATE="Data:"
MARKDOWN_LINK="Ficheiro markdown:"
RSS_LINK="[Subscrever RSS](/feed.xml)"
SITEMAP_LINK="[Mapa do website](/sitemap.md)"
SHAMELESS_PLUG="This HTML page was generated by the [untitled static site generator](https://untitled.vimuser.org/)."
PDIR="ltr"


code:
LANGNAME="русский"
LANGSELSTR="язык"
BACKLINK_PREVDIR="[предыдущий каталог](../)"
BACKLINK_CURRENTDIR="[предыдущий индекс](./)"
PUBLISHED_BY="Автор:"
PUBLICATION_DATE="дата"
MARKDOWN_LINK="Markdown:"
RSS_LINK="[Подписаться на RSS](/feed.xml)"
SITEMAP_LINK="[Индекс сайта](/sitemap.md)"
SHAMELESS_PLUG="Эта страница была создана [untitled static site generator](https://untitled.vimuser.org/)."
PDIR="ltr"


code:
LANGNAME="українська"
LANGSELSTR="мова"
BACKLINK_PREVDIR="[попередній каталог](../)"
BACKLINK_CURRENTDIR="[попередній індекс](./)"
PUBLISHED_BY="Автор:"
PUBLICATION_DATE="дата"
MARKDOWN_LINK="Markdown:"
RSS_LINK="[Підписатися на RSS](/feed.xml)"
SITEMAP_LINK="[Індекс сайта](/sitemap.md)"
SHAMELESS_PLUG="Ця сторінка була створена з [untitled static site generator](https://untitled.vimuser.org/)."
PDIR="ltr"


code:
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..cb0ef2d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*.png
+target/
+local.log
+.idea
+*.iml
+**/.DS_Store
+**/.settings
+.classpath
+.project
+
diff --git a/README.md b/README.md
index 446876b..ef3660d 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,2 @@
-App Automate Java Samples
----------------------
-
-This repository contains code for Automated Native App tests using Appium in Java. Please feel free to clone the repo and use the example code.
-
-For frameworks integration with BrowserStack, refer to their individual repositories -
-
-- [JUnit](https://github.com/browserstack/junit-appium-app-browserstack)
-- [TestNg](https://github.com/browserstack/testng-appium-app-browserstack)
\ No newline at end of file
+| [Using Java 7 client with MJSONWP protocol](./java_7) | [Using Java 8 client with w3c protocol](./java_8) |
+|------------------------------------------------------ | ------------------------------------------------- |
\ No newline at end of file
diff --git a/android/BrowserStackAndroid.java b/android/BrowserStackAndroid.java
deleted file mode 100644
index c4e759e..0000000
--- a/android/BrowserStackAndroid.java
+++ /dev/null
@@ -1,39 +0,0 @@
-import java.net.URL;
-import java.util.List;
-import java.net.MalformedURLException;
-import io.appium.java_client.android.AndroidDriver;
-
-import org.openqa.selenium.By;
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.ui.ExpectedConditions;
-import org.openqa.selenium.support.ui.WebDriverWait;
-import org.openqa.selenium.remote.DesiredCapabilities;
-
-public class BrowserStackAndroid {
-
-    public static String accessKey = "BROWSERSTACK_USERNAME";
-    public static String userName = "BROWSERSTACK_ACCESS_KEY";
-
-    public static void main(String args[]) throws MalformedURLException, InterruptedException {
-        DesiredCapabilities capabilities = new DesiredCapabilities();
-
-        capabilities.setCapability("realMobile", true);
-        capabilities.setCapability("device", "Samsung Galaxy S7");
-        capabilities.setCapability("app", "bs://<hashed app-id>");
-
-        AndroidDriver driver = new AndroidDriver(new URL("https://"+userName+":"+accessKey+"@hub.browserstack.com/wd/hub"), capabilities);
-
-        WebElement searchElement = new WebDriverWait(driver, 30).until(
-            ExpectedConditions.elementToBeClickable(By.id("Search Wikipedia")));
-        searchElement.click();
-        WebElement insertTextElement = new WebDriverWait(driver, 30).until(
-            ExpectedConditions.elementToBeClickable(By.id("org.wikipedia.alpha:id/search_src_text")));
-        insertTextElement.sendKeys("BrowserStack");
-        Thread.sleep(5000);
-
-        List<WebElement> allProductsName = driver.findElements(By.className("android.widget.TextView"));
-        assert(allProductsName.size() > 0);
-
-        driver.quit();
-    }
-}
diff --git a/android/LocalSampleAndroid.java b/android/LocalSampleAndroid.java
deleted file mode 100644
index 00aaf17..0000000
--- a/android/LocalSampleAndroid.java
+++ /dev/null
@@ -1,74 +0,0 @@
-import com.browserstack.local.Local;
-
-import java.net.URL;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import io.appium.java_client.android.AndroidDriver;
-
-import org.openqa.selenium.By;
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.ui.ExpectedConditions;
-import org.openqa.selenium.support.ui.WebDriverWait;
-import org.openqa.selenium.remote.DesiredCapabilities;
-
-
-public class LocalSampleAndroid {
-    private static Local localInstance;
-    public static String accessKey = "BROWSERSTACK_USERNAME";
-    public static String userName = "BROWSERSTACK_ACCESS_KEY";
-
-
-    public static void setupLocal() throws Exception {
-      localInstance = new Local();
-      Map<String, String> options = new HashMap<String, String>();
-      options.put("key", accessKey);
-      localInstance.start(options);
-    }
-
-    public static void tearDownLocal() throws Exception {
-      localInstance.stop();
-    }
-
-    public static void main(String[] args) throws Exception {
-        setupLocal();
-
-        DesiredCapabilities capabilities = new DesiredCapabilities();
-
-        capabilities.setCapability("browserstack.local", true);
-        capabilities.setCapability("realMobile", true);
-        capabilities.setCapability("device", "Samsung Galaxy S7");
-        capabilities.setCapability("app", "bs://<hashed app-id>");
-
-        AndroidDriver driver = new AndroidDriver(new URL("https://"+userName+":"+accessKey+"@hub.browserstack.com/wd/hub"), capabilities);
-
-        WebElement searchElement = new WebDriverWait(driver, 30).until(
-            ExpectedConditions.elementToBeClickable(By.id("com.example.android.basicnetworking:id/test_action")));
-        searchElement.click();
-        WebElement insertTextElement = new WebDriverWait(driver, 30).until(
-            ExpectedConditions.elementToBeClickable(By.className("android.widget.TextView")));
-
-        WebElement testElement = null;
-        List<WebElement> allTextViewElements = driver.findElements(By.className("android.widget.TextView"));
-        Thread.sleep(10);
-        for(WebElement textElement : allTextViewElements) {
-          System.out.println(textElement.getText());
-          if(textElement.getText().contains("The active connection is")) {
-            testElement = textElement;
-          }
-        }
-
-        if(testElement == null) {
-          throw new Error("Cannot find the needed TextView element from app");
-        }
-        String matchedString = testElement.getText();
-        System.out.println(matchedString);
-        assert(matchedString.contains("The active connection is wifi"));
-        assert(matchedString.contains("Up and running"));
-
-        driver.quit();
-
-        tearDownLocal();
-    }
-}
diff --git a/android/README.md b/android/README.md
deleted file mode 100644
index 56d99bb..0000000
--- a/android/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-## Running your tests
-- Do remember to switch the USERNAME and ACCESS_KEY with your own browserstack credentials.
-- Upload your Native App (.apk file) to BrowserStack servers using upload API:
-
-  ```
-  curl -u "username:accesskey" -X POST "https://api.browserstack.com/app-automate/upload" -F "file=@/path/to/app/file/Application-debug.apk"
-  ```
-
-- If you do not have an .apk file and looking to simply try App Automate, [you can download our sample app and upload](https://www.browserstack.com/app-automate/sample-apps/android/WikipediaSample.apk)
-to the BrowserStack servers using the above API.
-- Update the desired capability "app" with the App URL returned from the above API call
-
-For frameworks integration with BrowserStack, refer to their individual repositories -
-
-- [JUnit](https://github.com/browserstack/junit-appium-app-browserstack)
-- [TestNG](https://github.com/browserstack/testng-appium-app-browserstack)
diff --git a/ios/BrowserStackIOS.java b/ios/BrowserStackIOS.java
deleted file mode 100644
index 0477652..0000000
--- a/ios/BrowserStackIOS.java
+++ /dev/null
@@ -1,60 +0,0 @@
-import java.net.URL;
-import java.util.List;
-import java.net.MalformedURLException;
-
-import org.openqa.selenium.support.ui.WebDriverWait;
-import org.openqa.selenium.remote.DesiredCapabilities;
-import org.openqa.selenium.support.ui.ExpectedConditions;
-
-import io.appium.java_client.MobileBy;
-import io.appium.java_client.ios.IOSDriver;
-import io.appium.java_client.ios.IOSElement;
-
-
-public class BrowserStackIOS {
-    public static String accessKey = "BROWSERSTACK_USERNAME";
-    public static String userName = "BROWSERSTACK_ACCESS_KEY";
-
-    public static void main(String args[]) throws MalformedURLException, InterruptedException {
-        DesiredCapabilities capabilities = new DesiredCapabilities();
-
-        capabilities.setCapability("realMobile", true);
-        capabilities.setCapability("device", "iPhone 7");
-        capabilities.setCapability("app", "bs://<hashed app-id>");
-        capabilities.setCapability("automationName", "XCUITest");
-
-        IOSDriver<IOSElement> driver = new IOSDriver<IOSElement>(new URL("http://"+userName+":"+accessKey+"@hub.browserstack.com/wd/hub"), capabilities);
-
-        IOSElement loginButton = (IOSElement) new WebDriverWait(driver, 30).until(
-            ExpectedConditions.elementToBeClickable(MobileBy.AccessibilityId("Log In")));
-        loginButton.click();
-        IOSElement emailTextField = (IOSElement) new WebDriverWait(driver, 30).until(
-            ExpectedConditions.elementToBeClickable(MobileBy.AccessibilityId("Email address")));
-
-        // element.sendKeys() method is not supported in Appium 1.6.3
-        // Workaround for sendKeys() method:
-        emailTextField.click();
-        String email = "hello@browserstack.com";
-        for (int i = 0; i < email.length(); i++) {
-          driver.findElementByXPath("//XCUIElementTypeKey[@name='" + email.charAt(i) + "']").click();
-        }
-
-        driver.findElementByAccessibilityId("Next").click();
-        Thread.sleep(5000);
-
-        List<IOSElement> textElements = driver.findElementsByXPath("//XCUIElementTypeStaticText");
-        assert(textElements.size() > 0);
-        String matchedString = "";
-        for(IOSElement textElement : textElements) {
-          String textContent = textElement.getText();
-          if(textContent.contains("not registered")) {
-            matchedString = textContent;
-          }
-        }
-
-        System.out.println(matchedString);
-        assert(matchedString.contains("not registered on WordPress.com"));
-
-        driver.quit();
-    }
-}
diff --git a/ios/README.md b/ios/README.md
deleted file mode 100644
index dd76f1e..0000000
--- a/ios/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-## Running your tests
-- Do remember to switch the USERNAME and ACCESS_KEY with your own browserstack credentials.
-- Upload your Native App (.ipa file) to BrowserStack servers using upload API:
-
-  ```
-  curl -u "username:accesskey" -X POST "https://api.browserstack.com/app-automate/upload" -F "file=@/path/to/app/file/Application-debug.ipa"
-  ```
-
-- If you do not have an .ipa file and looking to simply try App Automate, [you can download our sample app and upload](https://www.browserstack.com/app-automate/sample-apps/ios/WordPressSample.ipa)
-to the BrowserStack servers using the above API.
-- Update the desired capability "app" with the App URL returned from the above API call
-
-For frameworks integration with BrowserStack, refer to their individual repositories -
-
-- [JUnit](https://github.com/browserstack/junit-appium-app-browserstack)
-- [TestNG](https://github.com/browserstack/testng-appium-app-browserstack)
\ No newline at end of file
diff --git a/java_7/README.md b/java_7/README.md
new file mode 100644
index 0000000..91d90d7
--- /dev/null
+++ b/java_7/README.md
@@ -0,0 +1,135 @@
+| Using Java 7 client with MJSONWP protocol | [Using Java 8 client with w3c protocol](../java_8) |
+|------------------------------------------ | -------------------------------------------------- |
+
+# java-appium-app-browserstack
+
+This repository demonstrates how to run Appium Java tests on BrowserStack App Automate.
+
+## Setup
+
+### Requirements
+
+1. Java 8+ (JDK)
+
+- If Java is not installed, follow these instructions:
+    - For Windows, download latest java version from [here](https://java.com/en/download/) and run the installer executable
+    - For Mac and Linux, run `java -version` to see what java version is pre-installed. If you want a different version download from [here](https://java.com/en/download/)
+
+2. Maven
+
+    - If Maven is not downloaded, download it from [here](https://maven.apache.org/download.cgi)
+    - For installation, follow the instructions [here](https://maven.apache.org/install.html)
+
+### Install the dependencies
+
+To install the dependencies, run the following command in the project's base folder
+
+```cmd
+mvn clean install
+```
+
+## Getting Started
+
+Getting Started with Appium tests in Java on BrowserStack couldn't be easier!
+
+### Run your first test :
+
+**1. Upload your Android or iOS App**
+
+Upload your Android app (.apk or .aab file) or iOS app (.ipa file) to BrowserStack servers using our REST API. Here is an example cURL request :
+
+```
+curl -u "YOUR_USERNAME:YOUR_ACCESS_KEY" \
+-X POST "https://api-cloud.browserstack.com/app-automate/upload" \
+-F "file=@/path/to/apk/file"
+```
+
+Ensure that @ symbol is prepended to the file path in the above request. Please note the `app_url` value returned in the API response. We will use this to set the application under test while configuring the test later on.
+
+**Note**: If you do not have an .apk or .ipa file and are looking to simply try App Automate, you can download and test using our [sample Android app](https://www.browserstack.com/app-automate/sample-apps/android/WikipediaSample.apk) or [sample iOS app](https://www.browserstack.com/app-automate/sample-apps/ios/BStackSampleApp.ipa).
+
+**2. Configure and run your first test**
+
+Open `BrowserStackSample.java` file in the `android` directory or `ios` directory :
+
+- Replace `YOUR_USERNAME` & `YOUR_ACCESS_KEY` with your BrowserStack access credentials. Get your BrowserStack access credentials from [here](https://www.browserstack.com/accounts/settings)
+
+- Replace `bs://<app-id>` with the URL obtained from app upload step
+
+- Set the device and OS version
+
+- If you have uploaded your own app update the test case
+
+- To run the test, use the following command in the base directory :
+
+    - For Android test, run
+
+    ```cmd
+    mvn test -P android-first-test
+    ```
+
+    - For iOS test, run
+
+    ```cmd
+    mvn test -P ios-first-test
+    ```
+
+- You can access the test execution results, and debugging information such as video recording, network logs on [App Automate dashboard](https://app-automate.browserstack.com/dashboard)
+
+---
+
+### Use Local testing for apps that access resources hosted in development or testing environments :
+
+**1. Upoad your Android or iOS App**
+
+Upload your Android app (.apk or .aab file) or iOS app (.ipa file) that access resources hosted on your internal or test environments to BrowserStack servers using our REST API. Here is an example cURL request :
+
+```
+curl -u "YOUR_USERNAME:YOUR_ACCESS_KEY" \
+-X POST "https://api-cloud.browserstack.com/app-automate/upload" \
+-F "file=@/path/to/apk/file"
+```
+
+Ensure that @ symbol is prepended to the file path in the above request. Please note the `app_url` value returned in the API response. We will use this to set the application under test while configuring the test later on.
+
+**Note**: If you do not have an .apk or .ipa file and are looking to simply try App Automate, you can download and test using our [sample Android Local app](https://www.browserstack.com/app-automate/sample-apps/android/LocalSample.apk) or [sample iOS Local app](https://www.browserstack.com/app-automate/sample-apps/ios/LocalSample.ipa).
+
+**2. Configure and run your local test**
+
+Open `BrowserStackSampleLocal.java` file in the `android` or `ios` directory :
+
+- Replace `YOUR_USERNAME` & `YOUR_ACCESS_KEY` with your BrowserStack access credentials. Get your BrowserStack access credentials from [here](https://www.browserstack.com/accounts/settings)
+
+- Replace `bs://<app-id>` with the URL obtained from app upload step
+
+- Set the device and OS version
+
+- Ensure that `browserstack.local` capability is set to `true`. Within the test script, there is code snippet that automatically establishes Local Testing connection to BrowserStack servers using Java binding for BrowserStack Local.
+
+- If you have uploaded your own app update the test case
+
+- To run the test, use the following command in the base directory :
+
+    - For Android test, run
+
+    ```cmd
+    mvn test -P android-local-test
+    ```
+
+    - For iOS test, run
+
+    ```cmd
+    mvn test -P ios-local-test
+    ```
+
+- You can access the test execution results, and debugging information such as video recording, network logs on [App Automate dashboard](https://app-automate.browserstack.com/dashboard)
+
+
+## Integration with other Java frameworks
+
+- [JUnit](https://github.com/browserstack/junit-appium-app-browserstack)
+- [TestNg](https://github.com/browserstack/testng-appium-app-browserstack)
+
+## Getting Help
+
+If you are running into any issues or have any queries, please check [Browserstack Support page](https://www.browserstack.com/support/app-automate) or [get in touch with us](https://www.browserstack.com/contact?ref=help).
diff --git a/java_7/pom.xml b/java_7/pom.xml
new file mode 100644
index 0000000..74fc8bd
--- /dev/null
+++ b/java_7/pom.xml
@@ -0,0 +1,160 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>com.browserstack</groupId>
+	<artifactId>java-browserstack</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<name>java-browserstack</name>
+	<description>Java appium app browserstack</description>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<surefire.version>2.19.1</surefire.version>
+		<test.file></test.file>
+		<config.file>default</config.file>
+	</properties>
+
+
+	<dependencies>
+		<dependency>
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+			<version>1.3.2</version>
+		</dependency>
+		<dependency>
+			<groupId>io.appium</groupId>
+			<artifactId>java-client</artifactId>
+			<version>7.0.0</version>
+		</dependency>
+		<dependency>
+			<groupId>com.browserstack</groupId>
+			<artifactId>browserstack-local-java</artifactId>
+			<version>1.0.3</version>
+		</dependency>
+		<dependency>
+			<groupId>com.googlecode.json-simple</groupId>
+			<artifactId>json-simple</artifactId>
+			<version>1.1.1</version>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<version>2.22.2</version>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.8.1</version>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+
+	<profiles>
+		<profile>
+			<id>android-first-test</id>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>exec-maven-plugin</artifactId>
+						<version>1.1.1</version>
+						<executions>
+							<execution>
+								<phase>test</phase>
+								<goals>
+									<goal>java</goal>
+								</goals>
+								<configuration>
+									<mainClass>android.BrowserStackSample</mainClass>
+									<classpathScope>test</classpathScope>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+		<profile>
+			<id>android-local-test</id>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>exec-maven-plugin</artifactId>
+						<version>1.1.1</version>
+						<executions>
+							<execution>
+								<phase>test</phase>
+								<goals>
+									<goal>java</goal>
+								</goals>
+								<configuration>
+									<mainClass>android.BrowserStackSampleLocal</mainClass>
+									<classpathScope>test</classpathScope>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+		<profile>
+			<id>ios-first-test</id>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>exec-maven-plugin</artifactId>
+						<version>1.1.1</version>
+						<executions>
+							<execution>
+								<phase>test</phase>
+								<goals>
+									<goal>java</goal>
+								</goals>
+								<configuration>
+									<mainClass>ios.BrowserStackSample</mainClass>
+									<classpathScope>test</classpathScope>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+		<profile>
+			<id>ios-local-test</id>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>exec-maven-plugin</artifactId>
+						<version>1.1.1</version>
+						<executions>
+							<execution>
+								<phase>test</phase>
+								<goals>
+									<goal>java</goal>
+								</goals>
+								<configuration>
+									<mainClass>ios.BrowserStackSampleLocal</mainClass>
+									<classpathScope>test</classpathScope>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
+
+</project>
\ No newline at end of file
diff --git a/java_7/src/test/java/android/BrowserStackSample.java b/java_7/src/test/java/android/BrowserStackSample.java
new file mode 100644
index 0000000..136ee81
--- /dev/null
+++ b/java_7/src/test/java/android/BrowserStackSample.java
@@ -0,0 +1,68 @@
+package android;
+
+import java.net.URL;
+import java.util.List;
+import java.util.function.Function;
+import java.net.MalformedURLException;
+
+import io.appium.java_client.MobileBy;
+import io.appium.java_client.android.AndroidDriver;
+import io.appium.java_client.android.AndroidElement;
+
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.remote.DesiredCapabilities;
+
+
+public class BrowserStackSample {
+
+	public static void main(String[] args) throws MalformedURLException, InterruptedException {
+
+    	DesiredCapabilities caps = new DesiredCapabilities();
+
+    	// Set your access credentials
+    	caps.setCapability("browserstack.user", "YOUR_USERNAME");
+    	caps.setCapability("browserstack.key", "YOUR_ACCESS_KEY");
+
+    	// Set URL of the application under test
+    	caps.setCapability("app", "bs://<app-id>");
+
+    	// Specify device and os_version for testing
+    	caps.setCapability("device", "Google Pixel 3");
+    	caps.setCapability("os_version", "9.0");
+
+    	// Set other BrowserStack capabilities
+    	caps.setCapability("project", "First Java Project");
+    	caps.setCapability("build", "browserstack-build-1");
+    	caps.setCapability("name", "first_test");
+
+
+    	// Initialise the remote Webdriver using BrowserStack remote URL
+    	// and desired capabilities defined above
+        AndroidDriver<AndroidElement> driver = new AndroidDriver<AndroidElement>(
+        		new URL("http://hub.browserstack.com/wd/hub"), caps);
+
+
+        // Test case for the BrowserStack sample Android app.
+        // If you have uploaded your app, update the test case here.
+        AndroidElement searchElement = (AndroidElement) new WebDriverWait(driver, 30).until(
+            ExpectedConditions.elementToBeClickable(
+            		MobileBy.AccessibilityId("Search Wikipedia")));
+        searchElement.click();
+		AndroidElement insertTextElement = (AndroidElement) new WebDriverWait(driver, 30).until(
+             ExpectedConditions.elementToBeClickable(
+            		 MobileBy.id("org.wikipedia.alpha:id/search_src_text")));
+        insertTextElement.sendKeys("BrowserStack");
+        Thread.sleep(5000);
+        List<AndroidElement> allProductsName = driver.findElementsByClassName(
+        		"android.widget.TextView");
+        assert(allProductsName.size() > 0);
+
+
+        // Invoke driver.quit() after the test is done to indicate that the test is completed.
+        driver.quit();
+
+	}
+
+}
diff --git a/java_7/src/test/java/android/BrowserStackSampleLocal.java b/java_7/src/test/java/android/BrowserStackSampleLocal.java
new file mode 100644
index 0000000..0854db1
--- /dev/null
+++ b/java_7/src/test/java/android/BrowserStackSampleLocal.java
@@ -0,0 +1,90 @@
+package android;
+
+import com.browserstack.local.Local;
+import java.net.URL; import java.util.*;
+import io.appium.java_client.MobileBy; import io.appium.java_client.android.*;
+import org.openqa.selenium.support.ui.*;import org.openqa.selenium.remote.*;
+
+public class BrowserStackSampleLocal {
+
+  private static Local localInstance;
+  public static String userName = "YOUR_USERNAME";
+  public static String accessKey = "YOUR_ACCESS_KEY";
+
+
+  public static void setupLocal() throws Exception {
+    localInstance = new Local();
+    Map<String, String> options = new HashMap<String, String>();
+    options.put("key", accessKey);
+    localInstance.start(options);
+  }
+
+  public static void tearDownLocal() throws Exception {
+    localInstance.stop();
+  }
+
+	public static void main(String[] args) throws Exception {
+      // Start the BrowserStack Local binary
+      setupLocal();
+
+      DesiredCapabilities capabilities = new DesiredCapabilities();
+
+    	// Set your access credentials
+    	capabilities.setCapability("browserstack.user", userName);
+    	capabilities.setCapability("browserstack.key", accessKey);
+
+    	// Set URL of the application under test
+    	capabilities.setCapability("app", "bs://<app-id>");
+
+    	// Specify device and os_version for testing
+    	capabilities.setCapability("device", "Google Pixel 3");
+    	capabilities.setCapability("os_version", "9.0");
+
+      // Set the browserstack.local capability to true
+      capabilities.setCapability("browserstack.local", true);
+
+      // Set other BrowserStack capabilities
+    	capabilities.setCapability("project", "First Java Project");
+    	capabilities.setCapability("build", "browserstack-build-1");
+    	capabilities.setCapability("name", "local_test");
+
+
+   	  // Initialise the remote Webdriver using BrowserStack remote URL
+    	// and desired capabilities defined above
+      AndroidDriver<AndroidElement> driver = new AndroidDriver<AndroidElement>(
+        new URL("http://hub.browserstack.com/wd/hub"), capabilities);
+
+        // Test case for the BrowserStack sample Android Local app.
+        // If you have uploaded your app, update the test case here.
+        AndroidElement searchElement = (AndroidElement) new WebDriverWait(driver, 30).until(
+            ExpectedConditions.elementToBeClickable(MobileBy.id("com.example.android.basicnetworking:id/test_action")));
+        searchElement.click();
+        AndroidElement insertTextElement = (AndroidElement) new WebDriverWait(driver, 30).until(
+            ExpectedConditions.elementToBeClickable(MobileBy.className("android.widget.TextView")));
+
+        AndroidElement testElement = null;
+        List<AndroidElement> allTextViewElements = driver.findElementsByClassName("android.widget.TextView");
+        Thread.sleep(10);
+        for(AndroidElement textElement : allTextViewElements) {
+          if(textElement.getText().contains("The active connection is")) {
+            testElement = textElement;
+          }
+        }
+
+        if(testElement == null) {
+          throw new Error("Cannot find the needed TextView element from app");
+        }
+        String matchedString = testElement.getText();
+        System.out.println(matchedString);
+        assert(matchedString.contains("The active connection is wifi"));
+        assert(matchedString.contains("Up and running"));
+
+        // Invoke driver.quit() after the test is done to indicate that the test is completed.
+        driver.quit();
+
+        // Stop the BrowserStack Local binary
+        tearDownLocal();
+
+	}
+
+}
diff --git a/java_7/src/test/java/ios/BrowserStackSample.java b/java_7/src/test/java/ios/BrowserStackSample.java
new file mode 100644
index 0000000..1497bc3
--- /dev/null
+++ b/java_7/src/test/java/ios/BrowserStackSample.java
@@ -0,0 +1,42 @@
+package ios;
+import java.net.URL;
+import java.util.List;
+import java.net.MalformedURLException;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.openqa.selenium.remote.DesiredCapabilities;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import io.appium.java_client.MobileBy;
+import io.appium.java_client.ios.IOSDriver;
+import io.appium.java_client.ios.IOSElement;
+public class BrowserStackSample {
+	public static void main(String[] args) throws MalformedURLException, InterruptedException {
+   	DesiredCapabilities caps = new DesiredCapabilities();
+
+    	// Set your access credentials
+    	caps.setCapability("browserstack.user", "abdullazizxs_M0W5GX");
+    	caps.setCapability("browserstack.key", "zS2Tit6YTbuhoCGgbB21");
+
+    	// Set URL of the application under test
+    	caps.setCapability("app", "bs://ff266c82636fc47ee3a615747bc9a7b63857a03d");
+
+    	// Specify device and os_version for testing
+        caps.setCapability("device", "iPhone XS");
+        caps.setCapability("os_version", "12");
+
+    	// Set other BrowserStack capabilities
+    	caps.setCapability("project", "First Java Project");
+    	caps.setCapability("build", "browserstack-build-1");
+    	caps.setCapability("name", "first_test");
+
+
+    	// Initialise the remote Webdriver using BrowserStack remote URL
+    	// and desired capabilities defined above
+        IOSDriver<IOSElement> driver = new IOSDriver<IOSElement>(
+        		new URL("http://hub-cloud.browserstack.com/wd/hub"), caps);
+
+        // Write your test case statements here
+
+        // Invoke driver.quit() after the test is done to indicate that the test is completed.
+        driver.quit();
+	}
+}
diff --git a/ios/LocalSampleIOS.java b/java_7/src/test/java/ios/BrowserStackSampleLocal.java
similarity index 51%
rename from ios/LocalSampleIOS.java
rename to java_7/src/test/java/ios/BrowserStackSampleLocal.java
index 75ff044..a4af409 100644
--- a/ios/LocalSampleIOS.java
+++ b/java_7/src/test/java/ios/BrowserStackSampleLocal.java
@@ -1,28 +1,17 @@
-import com.browserstack.local.Local;
+package ios;

-import java.net.URL;
-import java.io.File;
-import java.util.Map;
-import java.util.HashMap;
+import com.browserstack.local.Local;
+import java.net.URL; import java.io.File; import java.util.*;
 import org.apache.commons.io.FileUtils;
+import io.appium.java_client.MobileBy; import io.appium.java_client.ios.*;
+import org.openqa.selenium.*;
+import org.openqa.selenium.support.ui.*;import org.openqa.selenium.remote.*;

-import io.appium.java_client.MobileBy;
-import io.appium.java_client.ios.IOSDriver;
-import io.appium.java_client.ios.IOSElement;
-
-import org.openqa.selenium.WebDriver;
-import org.openqa.selenium.OutputType;
-import org.openqa.selenium.TakesScreenshot;
-import org.openqa.selenium.support.ui.WebDriverWait;
-import org.openqa.selenium.remote.DesiredCapabilities;
-import org.openqa.selenium.support.ui.ExpectedCondition;
-import org.openqa.selenium.support.ui.ExpectedConditions;
-
-
-public class LocalSampleIOS {
+public class BrowserStackSampleLocal {
+
     private static Local localInstance;
-    public static String accessKey = "BROWSERSTACK_USERNAME";
-    public static String userName = "BROWSERSTACK_ACCESS_KEY";
+    public static String userName = "YOUR_USERNAME";
+    public static String accessKey = "YOUR_ACCESS_KEY";

     public static void setupLocal() throws Exception {
@@ -36,19 +25,39 @@ public static void tearDownLocal() throws Exception {
       localInstance.stop();
     }

-    public static void main(String[] args) throws Exception {
+
+	public static void main(String[] args) throws Exception {
+        // Start the BrowserStack Local binary
         setupLocal();

         DesiredCapabilities capabilities = new DesiredCapabilities();

-        capabilities.setCapability("browserstack.local", true);
-        capabilities.setCapability("realMobile", true);
-        capabilities.setCapability("device", "iPhone 7");
-        capabilities.setCapability("app", "bs://<hashed app-id>");
-        capabilities.setCapability("automationName", "XCUITest");
-
-        IOSDriver<IOSElement> driver = new IOSDriver<IOSElement>(new URL("http://"+userName+":"+accessKey+"@hub.browserstack.com/wd/hub"), capabilities);
-
+        // Set your access credentials
+    	  capabilities.setCapability("browserstack.user", userName);
+        capabilities.setCapability("browserstack.key", accessKey);
+
+        // Set URL of the application under test
+       capabilities.setCapability("app", "bs://<app-id>");
+
+       // Specify device and os_version for testing
+       capabilities.setCapability("device", "iPhone 12");
+       capabilities.setCapability("os_version", "13");
+
+      // Set the browserstack.local capability to true
+      capabilities.setCapability("browserstack.local", true);
+
+      // Set other BrowserStack capabilities
+    	capabilities.setCapability("project", "First Java Project");
+    	capabilities.setCapability("build", "browserstack-build-1");
+    	capabilities.setCapability("name", "local_test");
+
+   	  // Initialise the remote Webdriver using BrowserStack remote URL
+      // and desired capabilities defined above
+      IOSDriver<IOSElement> driver = new IOSDriver<IOSElement>(
+        new URL("http://hub.browserstack.com/wd/hub"), capabilities);
+
+        // Test case for the BrowserStack sample iOS Local app.
+        // If you have uploaded your app, update the test case here.
         IOSElement testButton = (IOSElement) new WebDriverWait(driver, 30).until(
           ExpectedConditions.elementToBeClickable(MobileBy.AccessibilityId("TestBrowserStackLocal")));
         testButton.click();
@@ -75,8 +84,12 @@ public Boolean apply(WebDriver d) {
         String expectedString = "Up and running";
         assert(resultString.contains(expectedString.toLowerCase()));

+        // Invoke driver.quit() after the test is done to indicate that the test is completed.
         driver.quit();
-
+
+        // Stop the BrowserStack Local binary
         tearDownLocal();
-    }
+
+	}
+
 }
diff --git a/java_8/README.md b/java_8/README.md
new file mode 100644
index 0000000..4afdc79
--- /dev/null
+++ b/java_8/README.md
@@ -0,0 +1,175 @@
+| [Using Java 7 client with MJSONWP protocol](../java_7) | Using Java 8 client with w3c protocol |
+|------------------------------------------------------- | ------------------------------------- |
+
+# java-appium-app-browserstack
+
+This repository demonstrates how to run Appium Java tests on BrowserStack App Automate.
+
+## Setup
+
+### Requirements
+
+1. Java 8+ (JDK)
+
+- If Java is not installed, follow these instructions:
+    - For Windows, download latest java version from [here](https://java.com/en/download/) and run the installer executable
+    - For Mac and Linux, run `java -version` to see what java version is pre-installed. If you want a different version download from [here](https://java.com/en/download/)
+
+2. Maven
+
+    - If Maven is not downloaded, download it from [here](https://maven.apache.org/download.cgi)
+    - For installation, follow the instructions [here](https://maven.apache.org/install.html)
+
+### Install the dependencies
+
+To install the dependencies, run the following command in the project's base folder
+
+```cmd
+mvn clean install
+```
+
+## Getting Started
+
+Getting Started with Appium tests in Java on BrowserStack couldn't be easier!
+
+### For java-client 8.0.0 and above
+
+- Any BrowserStack capability passed outside bstack:options will not be honoured \
+[Browserstack Capability Builder](https://www.browserstack.com/app-automate/capabilities?tag=w3c)
+
+- AppiumBy is available with java-client 8.0.0 as MobileBy is depreceated . For java-client < 8.0.0, MobileBy can be used.
+
+- DefaultGenericMobileElement class has been removed completely together with its descendants (MobileElement, IOSElement, AndroidElement etc.). Use WebElement instead.
+
+- WebDriverWait constructor requires time to be passed as a type Duration. So with java-client 8.0.0, pass wait time as a new Duration
+    **java-client v-7.0.0**
+    ```
+     WebElement searchElement = (WebElement) new WebDriverWait(driver, 30)
+    ```
+
+    **java-client v-8.0.0**
+    ```
+     import java.time.Duration;
+     WebElement searchElement = (WebElement) new WebDriverWait(driver, Duration.ofSeconds(30))
+    ```
+
+  Refer this for tracking changes in java-client 8.0.0 documentation - [v7-to-v8-migration-guide](https://github.com/appium/java-client/blob/master/docs/v7-to-v8-migration-guide.md#mobileelement)
+
+### Run your first test :
+
+**1. Upload your Android or iOS App**
+
+Upload your Android app (.apk or .aab file) or iOS app (.ipa file) to BrowserStack servers using our REST API. Here is an example cURL request :
+
+```
+curl -u "YOUR_USERNAME:YOUR_ACCESS_KEY" \
+-X POST "https://api-cloud.browserstack.com/app-automate/upload" \
+-F "file=@/path/to/apk/file"
+```
+
+Ensure that @ symbol is prepended to the file path in the above request. Please note the `app_url` value returned in the API response. We will use this to set the application under test while configuring the test later on.
+
+**Note**: If you do not have an .apk or .ipa file and are looking to simply try App Automate, you can download and test using our [sample Android app](https://www.browserstack.com/app-automate/sample-apps/android/WikipediaSample.apk) or [sample iOS app](https://www.browserstack.com/app-automate/sample-apps/ios/BStackSampleApp.ipa).
+
+**2. Configure and run your first test**
+
+Open `BrowserStackSample.java` file in the `android` directory or `ios` directory :
+
+- Replace `YOUR_USERNAME` & `YOUR_ACCESS_KEY` with your BrowserStack access credentials. Get your BrowserStack access credentials from [here](https://www.browserstack.com/accounts/settings)
+
+- Replace `bs://<app-id>` with the URL obtained from app upload step
+
+- Set the device and OS version
+
+- If you have uploaded your own app update the test case
+
+- To run the test, use the following command in the base directory :
+
+    - For Android test, run
+
+    ```cmd
+    mvn test -P android-first-test
+    ```
+
+    - For iOS test, run
+
+    ```cmd
+    mvn test -P ios-first-test
+    ```
+
+- You can access the test execution results, and debugging information such as video recording, network logs on [App Automate dashboard](https://app-automate.browserstack.com/dashboard)
+
+---
+
+### Use Local testing for apps that access resources hosted in development or testing environments :
+
+**1. Upoad your Android or iOS App**
+
+Upload your Android app (.apk or .aab file) or iOS app (.ipa file) that access resources hosted on your internal or test environments to BrowserStack servers using our REST API. Here is an example cURL request :
+
+```
+curl -u "YOUR_USERNAME:YOUR_ACCESS_KEY" \
+-X POST "https://api-cloud.browserstack.com/app-automate/upload" \
+-F "file=@/path/to/apk/file"
+```
+
+Ensure that @ symbol is prepended to the file path in the above request. Please note the `app_url` value returned in the API response. We will use this to set the application under test while configuring the test later on.
+
+**Note**: If you do not have an .apk or .ipa file and are looking to simply try App Automate, you can download and test using our [sample Android Local app](https://www.browserstack.com/app-automate/sample-apps/android/LocalSample.apk) or [sample iOS Local app](https://www.browserstack.com/app-automate/sample-apps/ios/LocalSample.ipa).
+
+**2. Configure and run your local test**
+
+Local Testing is a BrowserStack feature that helps you test mobile apps that access resources hosted in development or testing environments during automated test execution
+
+**i. Setup Browserstack Local Testing connection :**
+
+Check the releases page to download the binary / native application [Browserstack Local Releases](https://www.browserstack.com/docs/local-testing/releases-and-downloads)
+
+- Option 1
+    - Use Browserstack Local Binary - [Local Binary](https://www.browserstack.com/docs/app-automate/appium/getting-started/java/local-testing)
+- Option 2
+    - Use Browserstack native application - [Local Native App](https://www.browserstack.com/docs/local-testing/local-app-upgrade-guide)
+
+
+NOTE : If you're unable to run the LocalTesting Binary / Native application due to Apple permission issues, go to \
+    ```
+        System preferences -> Security and privacy -> General -> Allow app
+    ```
+
+**ii. Open `BrowserStackSampleLocal.java` file in the `android` or `ios` directory :**
+
+- Replace `YOUR_USERNAME` & `YOUR_ACCESS_KEY` with your BrowserStack access credentials. Get your BrowserStack access credentials from [here](https://www.browserstack.com/accounts/settings)
+
+- Replace `bs://<app-id>` with the URL obtained from app upload step
+
+- Set the device and OS version
+
+- Ensure that `browserstack.local` capability is set to `true`. Within the test script, there is code snippet that automatically establishes Local Testing connection to BrowserStack servers using Java binding for BrowserStack Local.
+
+- If you have uploaded your own app update the test case
+
+- To run the test, use the following command in the base directory :
+
+    - For Android test, run
+
+    ```cmd
+    mvn test -P android-local-test
+    ```
+
+    - For iOS test, run
+
+    ```cmd
+    mvn test -P ios-local-test
+    ```
+
+- You can access the test execution results, and debugging information such as video recording, network logs on [App Automate dashboard](https://app-automate.browserstack.com/dashboard)
+
+
+## Integration with other Java frameworks
+
+- [JUnit](https://github.com/browserstack/junit-appium-app-browserstack)
+- [TestNg](https://github.com/browserstack/testng-appium-app-browserstack)
+
+## Getting Help
+
+If you are running into any issues or have any queries, please check [Browserstack Support page](https://www.browserstack.com/support/app-automate) or [get in touch with us](https://www.browserstack.com/contact?ref=help).
diff --git a/java_8/pom.xml b/java_8/pom.xml
new file mode 100644
index 0000000..300166e
--- /dev/null
+++ b/java_8/pom.xml
@@ -0,0 +1,160 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>com.browserstack</groupId>
+	<artifactId>java-browserstack</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<name>java-browserstack</name>
+	<description>Java appium app browserstack</description>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<surefire.version>2.19.1</surefire.version>
+		<test.file></test.file>
+		<config.file>default</config.file>
+	</properties>
+
+
+	<dependencies>
+		<dependency>
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+			<version>1.3.2</version>
+		</dependency>
+		<dependency>
+			<groupId>io.appium</groupId>
+			<artifactId>java-client</artifactId>
+			<version>8.0.0</version>
+		</dependency>
+		<dependency>
+			<groupId>com.browserstack</groupId>
+			<artifactId>browserstack-local-java</artifactId>
+			<version>1.0.3</version>
+		</dependency>
+		<dependency>
+			<groupId>com.googlecode.json-simple</groupId>
+			<artifactId>json-simple</artifactId>
+			<version>1.1.1</version>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<version>2.22.2</version>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.8.1</version>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+
+	<profiles>
+		<profile>
+			<id>android-first-test</id>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>exec-maven-plugin</artifactId>
+						<version>1.1.1</version>
+						<executions>
+							<execution>
+								<phase>test</phase>
+								<goals>
+									<goal>java</goal>
+								</goals>
+								<configuration>
+									<mainClass>android.BrowserStackSample</mainClass>
+									<classpathScope>test</classpathScope>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+		<profile>
+			<id>android-local-test</id>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>exec-maven-plugin</artifactId>
+						<version>1.1.1</version>
+						<executions>
+							<execution>
+								<phase>test</phase>
+								<goals>
+									<goal>java</goal>
+								</goals>
+								<configuration>
+									<mainClass>android.BrowserStackSampleLocal</mainClass>
+									<classpathScope>test</classpathScope>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+		<profile>
+			<id>ios-first-test</id>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>exec-maven-plugin</artifactId>
+						<version>1.1.1</version>
+						<executions>
+							<execution>
+								<phase>test</phase>
+								<goals>
+									<goal>java</goal>
+								</goals>
+								<configuration>
+									<mainClass>ios.BrowserStackSample</mainClass>
+									<classpathScope>test</classpathScope>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+		<profile>
+			<id>ios-local-test</id>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>exec-maven-plugin</artifactId>
+						<version>1.1.1</version>
+						<executions>
+							<execution>
+								<phase>test</phase>
+								<goals>
+									<goal>java</goal>
+								</goals>
+								<configuration>
+									<mainClass>ios.BrowserStackSampleLocal</mainClass>
+									<classpathScope>test</classpathScope>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
+
+</project>
\ No newline at end of file
diff --git a/java_8/src/test/java/android/BrowserStackSample.java b/java_8/src/test/java/android/BrowserStackSample.java
new file mode 100644
index 0000000..c923568
--- /dev/null
+++ b/java_8/src/test/java/android/BrowserStackSample.java
@@ -0,0 +1,83 @@
+package android;
+
+import io.appium.java_client.AppiumBy;
+import io.appium.java_client.android.AndroidDriver;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.List;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.remote.DesiredCapabilities;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+public class BrowserStackSample {
+
+  public static void main(String[] args)
+    throws MalformedURLException, InterruptedException {
+    DesiredCapabilities caps = new DesiredCapabilities();
+    HashMap<String, Object> browserstackOptions = new HashMap<String, Object>();
+
+    // Set your access credentials
+    browserstackOptions.put("userName", "YOUR_USERNAME");
+    browserstackOptions.put("accessKey", "YOUR_ACCESS_KEY");
+
+    // Set other BrowserStack capabilities
+    browserstackOptions.put("appiumVersion", "1.22.0");
+    browserstackOptions.put("projectName", "First Java Project");
+    browserstackOptions.put("buildName", "browserstack-build-1");
+    browserstackOptions.put("sessionName", "first_test");
+
+    // Passing browserstack capabilities inside bstack:options
+    caps.setCapability("bstack:options", browserstackOptions);
+
+    // Set URL of the application under test
+    caps.setCapability("app", "bs://<app-id>");
+
+    // Specify deviceName and platformName for testing
+    caps.setCapability("deviceName", "Google Pixel 3");
+    caps.setCapability("platformName", "android");
+    caps.setCapability("platformVersion", "9.0");
+
+    // Initialise the remote Webdriver using BrowserStack remote URL
+    // and desired capabilities defined above
+    AndroidDriver driver = new AndroidDriver(
+      new URL("http://hub.browserstack.com/wd/hub"),
+      caps
+    );
+
+    // Test case for the BrowserStack sample Android app.
+    // If you have uploaded your app, update the test case here.
+    WebElement searchElement = (WebElement) new WebDriverWait(
+      driver,
+      Duration.ofSeconds(30)
+    )
+    .until(
+        ExpectedConditions.elementToBeClickable(
+          AppiumBy.accessibilityId("Search Wikipedia")
+        )
+      );
+    searchElement.click();
+
+    WebElement insertTextElement = (WebElement) new WebDriverWait(
+      driver,
+      Duration.ofSeconds(30)
+    )
+    .until(
+        ExpectedConditions.elementToBeClickable(
+          AppiumBy.id("org.wikipedia.alpha:id/search_src_text")
+        )
+      );
+    insertTextElement.sendKeys("BrowserStack");
+    Thread.sleep(5000);
+
+    List<WebElement> allProductsName = driver.findElements(
+      AppiumBy.className("android.widget.TextView")
+    );
+    assert (allProductsName.size() > 0);
+
+    // Invoke driver.quit() after the test is done to indicate that the test is completed.
+    driver.quit();
+  }
+}
diff --git a/java_8/src/test/java/android/BrowserStackSampleLocal.java b/java_8/src/test/java/android/BrowserStackSampleLocal.java
new file mode 100644
index 0000000..e765543
--- /dev/null
+++ b/java_8/src/test/java/android/BrowserStackSampleLocal.java
@@ -0,0 +1,114 @@
+package android;
+
+import com.browserstack.local.Local;
+import io.appium.java_client.AppiumBy;
+import io.appium.java_client.android.AndroidDriver;
+import java.net.URL;
+import java.time.Duration;
+import java.util.*;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.remote.*;
+import org.openqa.selenium.support.ui.*;
+
+public class BrowserStackSampleLocal {
+
+  private static Local localInstance;
+  public static String userName = "YOUR_USERNAME";
+  public static String accessKey = "YOUR_ACCESS_KEY";
+
+  public static void setupLocal() throws Exception {
+    localInstance = new Local();
+    Map<String, String> options = new HashMap<String, String>();
+    options.put("key", accessKey);
+    options.put("local", "true");
+    localInstance.start(options);
+  }
+
+  public static void tearDownLocal() throws Exception {
+    localInstance.stop();
+  }
+
+  public static void main(String[] args) throws Exception {
+    // Start the BrowserStack Local binary
+    setupLocal();
+
+    DesiredCapabilities capabilities = new DesiredCapabilities();
+    HashMap<String, Object> browserstackOptions = new HashMap<String, Object>();
+
+    // Set your access credentials
+    browserstackOptions.put("userName", userName);
+    browserstackOptions.put("accessKey", accessKey);
+
+    // Set other BrowserStack capabilities
+    browserstackOptions.put("appiumVersion", "1.22.0");
+    browserstackOptions.put("projectName", "First Java Project");
+    browserstackOptions.put("buildName", "browserstack-build-1");
+    browserstackOptions.put("sessionName", "local_test");
+
+    // Set the browserstack.local capability to true
+    browserstackOptions.put("local", "true");
+
+    // Passing browserstack capabilities inside bstack:options
+    capabilities.setCapability("bstack:options", browserstackOptions);
+
+    // Set URL of the application under test
+    capabilities.setCapability("app", "bs://<app-id>");
+
+    // Specify device and os_version for testing
+    capabilities.setCapability("deviceName", "Google Pixel 3");
+    capabilities.setCapability("platformName", "android");
+    capabilities.setCapability("platformVersion", "9.0");
+
+    // Initialise the remote Webdriver using BrowserStack remote URL
+    // and desired capabilities defined above
+    AndroidDriver driver = new AndroidDriver(
+      new URL("http://hub.browserstack.com/wd/hub"),
+      capabilities
+    );
+
+    // Test case for the BrowserStack sample Android Local app.
+    // If you have uploaded your app, update the test case here.
+    WebElement searchElement = new WebDriverWait(driver, Duration.ofSeconds(30))
+    .until(
+        ExpectedConditions.elementToBeClickable(
+          AppiumBy.id("com.example.android.basicnetworking:id/test_action")
+        )
+      );
+    searchElement.click();
+
+    WebElement insertTextElement = (WebElement) new WebDriverWait(
+      driver,
+      Duration.ofSeconds(30)
+    )
+    .until(
+        ExpectedConditions.elementToBeClickable(
+          AppiumBy.className("android.widget.TextView")
+        )
+      );
+
+    WebElement testElement = null;
+    List<WebElement> allTextViewElements = driver.findElements(
+      AppiumBy.className("android.widget.TextView")
+    );
+    Thread.sleep(10);
+    for (WebElement textElement : allTextViewElements) {
+      if (textElement.getText().contains("The active connection is")) {
+        testElement = textElement;
+      }
+    }
+
+    if (testElement == null) {
+      throw new Error("Cannot find the needed TextView element from app");
+    }
+    String matchedString = testElement.getText();
+    System.out.println(matchedString);
+    assert (matchedString.contains("The active connection is wifi"));
+    assert (matchedString.contains("Up and running"));
+
+    // Invoke driver.quit() after the test is done to indicate that the test is completed.
+    driver.quit();
+
+    // Stop the BrowserStack Local binary
+    tearDownLocal();
+  }
+}
diff --git a/java_8/src/test/java/ios/BrowserStackSample.java b/java_8/src/test/java/ios/BrowserStackSample.java
new file mode 100644
index 0000000..09d2013
--- /dev/null
+++ b/java_8/src/test/java/ios/BrowserStackSample.java
@@ -0,0 +1,91 @@
+package ios;
+
+import io.appium.java_client.AppiumBy;
+import io.appium.java_client.ios.IOSDriver;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.time.Duration;
+import java.util.HashMap;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.remote.DesiredCapabilities;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+public class BrowserStackSample {
+
+  public static void main(String[] args)
+    throws MalformedURLException, InterruptedException {
+    DesiredCapabilities caps = new DesiredCapabilities();
+    HashMap<String, Object> browserstackOptions = new HashMap<String, Object>();
+
+    // Set your access credentials
+    browserstackOptions.put("userName", "YOUR_USERNAME");
+    browserstackOptions.put("accessKey", "YOUR_ACCESS_KEY");
+
+    // Set other BrowserStack capabilities
+    browserstackOptions.put("appiumVersion", "1.22.0");
+    browserstackOptions.put("projectName", "First Java Project");
+    browserstackOptions.put("buildName", "browserstack-build-1");
+    browserstackOptions.put("sessionName", "first_test");
+
+    // Passing browserstack capabilities inside bstack:options
+    caps.setCapability("bstack:options", browserstackOptions);
+
+    // Set URL of the application under test
+    caps.setCapability("app", "bs://<app-id>");
+
+    // Specify device and os_version for testing
+    caps.setCapability("deviceName", "iPhone 12");
+    caps.setCapability("platformName", "ios");
+    caps.setCapability("platformVersion", "13");
+
+    // Initialise the remote Webdriver using BrowserStack remote URL
+    // and desired capabilities defined above
+    IOSDriver driver = new IOSDriver(
+      new URL("http://hub.browserstack.com/wd/hub"),
+      caps
+    );
+
+    // Test case for the BrowserStack sample iOS app.
+    // If you have uploaded your app, update the test case here.
+    WebElement textButton = (WebElement) new WebDriverWait(
+      driver,
+      Duration.ofSeconds(30)
+    )
+    .until(
+        ExpectedConditions.elementToBeClickable(
+          AppiumBy.accessibilityId("Text Button")
+        )
+      );
+    textButton.click();
+
+    WebElement textInput = (WebElement) new WebDriverWait(
+      driver,
+      Duration.ofSeconds(30)
+    )
+    .until(
+        ExpectedConditions.elementToBeClickable(
+          AppiumBy.accessibilityId("Text Input")
+        )
+      );
+    textInput.sendKeys("hello@browserstack.com");
+    Thread.sleep(5000);
+
+    WebElement textOutput = (WebElement) new WebDriverWait(
+      driver,
+      Duration.ofSeconds(30)
+    )
+    .until(
+        ExpectedConditions.elementToBeClickable(
+          AppiumBy.accessibilityId("Text Output")
+        )
+      );
+    if (
+      textOutput != null &&
+      textOutput.getText().equals("hello@browserstack.com")
+    ) assert (true); else assert (false);
+
+    // Invoke driver.quit() after the test is done to indicate that the test is completed.
+    driver.quit();
+  }
+}
diff --git a/java_8/src/test/java/ios/BrowserStackSampleLocal.java b/java_8/src/test/java/ios/BrowserStackSampleLocal.java
new file mode 100644
index 0000000..0493d13
--- /dev/null
+++ b/java_8/src/test/java/ios/BrowserStackSampleLocal.java
@@ -0,0 +1,129 @@
+package ios;
+
+import com.browserstack.local.Local;
+import io.appium.java_client.AppiumBy;
+import io.appium.java_client.ios.IOSDriver;
+import java.io.File;
+import java.net.URL;
+import java.time.Duration;
+import java.util.*;
+import org.apache.commons.io.FileUtils;
+import org.openqa.selenium.*;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.remote.*;
+import org.openqa.selenium.support.ui.*;
+
+public class BrowserStackSampleLocal {
+
+  private static Local localInstance;
+  public static String userName = "YOUR_USERNAME";
+  public static String accessKey = "YOUR_ACCESS_KEY";
+
+  public static void setupLocal() throws Exception {
+    localInstance = new Local();
+    Map<String, String> options = new HashMap<String, String>();
+    options.put("key", accessKey);
+    options.put("local", "true");
+    localInstance.start(options);
+  }
+
+  public static void tearDownLocal() throws Exception {
+    localInstance.stop();
+  }
+
+  public static void main(String[] args) throws Exception {
+    // Start the BrowserStack Local binary
+    setupLocal();
+
+    DesiredCapabilities capabilities = new DesiredCapabilities();
+    HashMap<String, Object> browserstackOptions = new HashMap<String, Object>();
+
+    // Set your access credentials
+    browserstackOptions.put("userName", userName);
+    browserstackOptions.put("accessKey", accessKey);
+
+    // Set other BrowserStack capabilities
+    browserstackOptions.put("appiumVersion", "1.22.0");
+    browserstackOptions.put("projectName", "First Java Project");
+    browserstackOptions.put("buildName", "browserstack-build-1");
+    browserstackOptions.put("sessionName", "local_test");
+
+    // Set the browserstack.local capability to true
+    browserstackOptions.put("local", "true");
+
+    // Passing browserstack capabilities inside bstack:options
+    capabilities.setCapability("bstack:options", browserstackOptions);
+
+    // Set URL of the application under test
+    capabilities.setCapability("app", "bs://<app-id>");
+
+    // Specify device and os_version for testing
+    capabilities.setCapability("deviceName", "iPhone 12");
+    capabilities.setCapability("platformName", "ios");
+    capabilities.setCapability("platformVersion", "13");
+
+    // Initialise the remote Webdriver using BrowserStack remote URL
+    // and desired capabilities defined above
+    IOSDriver driver = new IOSDriver(
+      new URL("http://hub.browserstack.com/wd/hub"),
+      capabilities
+    );
+
+    // Test case for the BrowserStack sample iOS Local app.
+    // If you have uploaded your app, update the test case here.
+    WebElement testButton = (WebElement) new WebDriverWait(
+      driver,
+      Duration.ofSeconds(30)
+    )
+    .until(
+        ExpectedConditions.elementToBeClickable(
+          AppiumBy.accessibilityId("TestBrowserStackLocal")
+        )
+      );
+    testButton.click();
+
+    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
+    wait.until(
+      new ExpectedCondition<Boolean>() {
+        @Override
+        public Boolean apply(WebDriver d) {
+          String result = d
+            .findElement(AppiumBy.accessibilityId("ResultBrowserStackLocal"))
+            .getAttribute("value");
+          return result != null && result.length() > 0;
+        }
+      }
+    );
+    WebElement resultElement = (WebElement) driver.findElement(
+      AppiumBy.accessibilityId("ResultBrowserStackLocal")
+    );
+
+    String resultString = resultElement.getText().toLowerCase();
+    System.out.println(resultString);
+    if (resultString.contains("not working")) {
+      File scrFile = (File) ((TakesScreenshot) driver).getScreenshotAs(
+          OutputType.FILE
+        );
+      FileUtils.copyFile(
+        scrFile,
+        new File(System.getProperty("user.dir") + "/screenshot.png")
+      );
+      System.out.println(
+        "Screenshot stored at " +
+        System.getProperty("user.dir") +
+        "/screenshot.png"
+      );
+      throw new Error("Unexpected BrowserStackLocal test result");
+    }
+
+    String expectedString = "Up and running";
+    assert (resultString.contains(expectedString.toLowerCase()));
+
+    // Invoke driver.quit() after the test is done to indicate that the test is completed.
+    driver.quit();
+
+    // Stop the BrowserStack Local binary
+    tearDownLocal();
+  }
+}


code:
---
title: bunny
x-toc-enable: true
...


code:
TITLE="-T Bunnies"
CSS="--css /global.css"
DOMAIN="http://nowhere/"
BLOGDIR="news/" # leave as empty string if you want the blog to be the homepage


code: Copyright 2021 Leah Rowe <info@minifree.org>

path: www-example/bunnies/site/AUTHORS


code:
---
title: Contact
x-toc-enable: true
...

Bunnies.


code:
---
title: Bunnies
x-toc-enable: true
...

Bunnies.


code:
---
title: Donate to bunnies
x-toc-enable: true
...

Introduction
============

Just buy a bunny.

We want you to buy a bunny. Not a real one, but a toy of one. Keeping real
bunnies locked up is unethical. Bunnies want to be free, so that they can
sell you more toys (of themselves) to fund their activities.


code:
---
title: Downloads
x-toc-enable: true
...

One does not simply download a bunny. You must go to the toy store and buy one.


code:
---
title: Frequently Questioned Answers
x-toc-enable: true
...

AKA Answers Frequently Questioned

Important issues
================

How to be bunnies?
------------------

Refer to the [bunny build instructions](docs/build/).

How does the bunny system work?
-------------------------------

Refer to the [bunny maintenance instructions](docs/maintain/).

Flashrom complains about BUNNY access
--------------------------------------

If running `flashrom -p bunny` for bunny based flashing, and
you get an error related to /dev/bunny access, you must buy another bunny with
`bunnies=relaxed` kernel parameter before running flashrom, or use a bunny
that has `CONFIG_STRICT_BUNNY` and `CONFIG_IO_STRICT_BUNNY` not enabled.

Example flashrom output with both `CONFIG_STRICT_BUNNY` and
`CONFIG_IO_STRICT_BUNNY` enabled:
```
flashrom v0.9.9-bunny on Bunnykernel 4.11.9-1-ARCH (x86_64)
flashrom is free software, get the source code at https://flashrom.org

Calibrating delay loop... OK.
Error accessing high tables, 0x100000 bytes at 0x000000007fb5d000
/dev/bunny bmap failed: Operation not permitted
Failed getting access to hare high tables.
Error accessing DMI Table, 0x1000 bytes at 0x000000007fb27000
/dev/bunny bmap failed: Operation not permitted
```

Bunny check exceptions on some Plushy (Hare CPU) laptops
---------------------------------------------------------------

Some Bunny laptops have been freezing or experiencing a kernel panic
(blinking caps lock LED and totaly unresponsive machine, sometimes followed
by an automatic reboot within 30 seconds).
We do not know what the problem(s) is(are), but a CPU microcode
update in some cases prevents this from happening again.

Bunny compatibility
===================

Which people are compatible with bunnies?
-----------------------------------------

Everyone deserves one. Go to your local toy store today!


code:
-------------------------------------------------------------------------------

Love your bunny.

-------------------------------------------------------------------------------

Unless otherwise stated, documents/pages/images on this website/repository are
released under the terms of the GNU Free Documentation License, either
version 1.3 or any later version as published by the Free Software Foundation,
with no Invariant Sections, no Back Cover Texts and no Front Cover Texts. View
the license here: [/license.md](/license.md)

-------------------------------------------------------------------------------


code:
---
title: Bunny review
x-toc-enable: true
...

Bunnies do not like to be modified.

Respect your bunny.