Initial commit
7
.editorconfig
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*.{kt,kts}]
|
||||||
|
ktlint_standard_no-unused-imports = disabled
|
||||||
|
ktlint_standard_no-wildcard-imports = disabled
|
||||||
|
ij_kotlin_allow_trailing_comma = true
|
||||||
|
ij_kotlin_allow_trailing_comma_on_call_site = true
|
10
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
- package-ecosystem: "gradle"
|
||||||
|
directory: "/" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
134
.github/update_google_play_listing.py
vendored
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
import httplib2
|
||||||
|
import os
|
||||||
|
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
from googleapiclient.discovery import build
|
||||||
|
from googleapiclient.errors import (
|
||||||
|
HttpError,
|
||||||
|
)
|
||||||
|
|
||||||
|
from oauth2client.service_account import ServiceAccountCredentials
|
||||||
|
|
||||||
|
|
||||||
|
dir = "listing/google/"
|
||||||
|
|
||||||
|
|
||||||
|
def load_listing_full_description(folder):
|
||||||
|
file_path = dir + folder + "/store_description.html"
|
||||||
|
with open(file_path, 'r') as file:
|
||||||
|
data = file.read()
|
||||||
|
return {
|
||||||
|
'listing_store_google_full_description': data,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def load_listing_texts(folder):
|
||||||
|
file_path = dir + folder + "/store_text.xml"
|
||||||
|
with open(file_path, 'r') as file:
|
||||||
|
data = file.read()
|
||||||
|
# Parse xml to dictionary
|
||||||
|
soup = BeautifulSoup(data, features="xml")
|
||||||
|
return {el["name"]: el.string for el in soup.find_all('string')}
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Load the listings from a folder to a human-accessible
|
||||||
|
dictionary object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def load_listing(folder):
|
||||||
|
a = load_listing_full_description(folder)
|
||||||
|
b = load_listing_texts(folder)
|
||||||
|
return a | b
|
||||||
|
|
||||||
|
|
||||||
|
package_name = "com.artemchep.keyguard"
|
||||||
|
|
||||||
|
language_base = "base"
|
||||||
|
language_mapping = {
|
||||||
|
'uk-UA': 'uk',
|
||||||
|
'ca-ES': 'ca',
|
||||||
|
'vi-VN': 'vi',
|
||||||
|
}
|
||||||
|
|
||||||
|
listing_base = load_listing(language_base)
|
||||||
|
listings = {}
|
||||||
|
|
||||||
|
for folder in os.listdir(dir):
|
||||||
|
listing = load_listing(folder)
|
||||||
|
# We check if this listing is different from the base
|
||||||
|
# listing. If so, we do not want to add it.
|
||||||
|
if listing == listing_base:
|
||||||
|
continue
|
||||||
|
# Fix language tag.
|
||||||
|
bcp_47_tag = folder.replace("-r", "-")
|
||||||
|
google_play_tag = language_mapping.get(bcp_47_tag, bcp_47_tag)
|
||||||
|
# Append.
|
||||||
|
listings[google_play_tag] = listing
|
||||||
|
|
||||||
|
print("Loaded listings for languages:")
|
||||||
|
for language in listings.keys():
|
||||||
|
print("- " + language)
|
||||||
|
print()
|
||||||
|
|
||||||
|
credentials = ServiceAccountCredentials.from_json_keyfile_name(
|
||||||
|
"service-account-google.json",
|
||||||
|
scopes="https://www.googleapis.com/auth/androidpublisher",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an httplib2.Http object to handle our HTTP requests and authorize
|
||||||
|
# it with the Credentials.
|
||||||
|
http = httplib2.Http()
|
||||||
|
http = credentials.authorize(http)
|
||||||
|
|
||||||
|
service = build("androidpublisher", "v3", http=http)
|
||||||
|
|
||||||
|
print("Uploading:")
|
||||||
|
edit = service.edits().insert(packageName=package_name).execute()
|
||||||
|
edit_id = edit["id"]
|
||||||
|
|
||||||
|
for language, listing in listings.items():
|
||||||
|
listing_request = {
|
||||||
|
'language': language,
|
||||||
|
'title': listing["listing_store_google_app_name"],
|
||||||
|
'shortDescription': listing["listing_store_google_short_description"],
|
||||||
|
'fullDescription': listing["listing_store_google_full_description"],
|
||||||
|
}
|
||||||
|
listing_response = None
|
||||||
|
try:
|
||||||
|
# See:
|
||||||
|
# https://developers.google.com/android-publisher/api-ref/rest/v3/edits.listings/patch
|
||||||
|
listing_response = service.edits()\
|
||||||
|
.listings()\
|
||||||
|
.patch(
|
||||||
|
packageName=package_name,
|
||||||
|
editId=edit_id,
|
||||||
|
language=language,
|
||||||
|
body=listing_request,
|
||||||
|
).execute()
|
||||||
|
except HttpError as e:
|
||||||
|
if e.status_code == 404:
|
||||||
|
# See:
|
||||||
|
# https://developers.google.com/android-publisher/api-ref/rest/v3/edits.listings/update
|
||||||
|
listing_response = service.edits()\
|
||||||
|
.listings()\
|
||||||
|
.update(
|
||||||
|
packageName=package_name,
|
||||||
|
editId=edit_id,
|
||||||
|
language=language,
|
||||||
|
body=listing_request,
|
||||||
|
).execute()
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
print("- " + language + " done!")
|
||||||
|
|
||||||
|
|
||||||
|
service.edits()\
|
||||||
|
.commit(
|
||||||
|
packageName=package_name,
|
||||||
|
editId=edit_id,
|
||||||
|
).execute()
|
||||||
|
|
||||||
|
print("Success!")
|
4
.github/update_google_play_listing.requirements.txt
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
google-api-python-client
|
||||||
|
oauth2client
|
||||||
|
beautifulsoup4
|
||||||
|
lxml
|
14
.github/update_gpm_passkeys_priv_apps.py
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
URL_APPS = "https://www.gstatic.com/gpm-passkeys-privileged-apps/apps.json"
|
||||||
|
|
||||||
|
response = requests.get(
|
||||||
|
URL_APPS,
|
||||||
|
)
|
||||||
|
|
||||||
|
json_obj = response.json()
|
||||||
|
json_text = json.dumps(json_obj, indent=2)
|
||||||
|
|
||||||
|
with open('common/src/commonMain/resources/MR/files/gpm_passkeys_privileged_apps.json', 'w') as f:
|
||||||
|
f.write(json_text)
|
1
.github/update_gpm_passkeys_priv_apps.requirements.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
requests
|
35
.github/update_justdeleteme.py
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
URL_JDM = "https://raw.githubusercontent.com/jdm-contrib/jdm/master/_data/sites.json"
|
||||||
|
|
||||||
|
response = requests.get(
|
||||||
|
URL_JDM,
|
||||||
|
)
|
||||||
|
|
||||||
|
aggr = []
|
||||||
|
|
||||||
|
for site in response.json():
|
||||||
|
entry = {
|
||||||
|
'name': site['name'],
|
||||||
|
'domains': site.get('domains', []),
|
||||||
|
}
|
||||||
|
|
||||||
|
def append_if_not_empty(src_key, dst_key):
|
||||||
|
if src_key in site and site[src_key]:
|
||||||
|
entry[dst_key] = site[src_key]
|
||||||
|
|
||||||
|
append_if_not_empty('url', 'url')
|
||||||
|
append_if_not_empty('difficulty', 'difficulty')
|
||||||
|
append_if_not_empty('notes', 'notes')
|
||||||
|
# for the extra-asshole websites
|
||||||
|
append_if_not_empty('email', 'email')
|
||||||
|
append_if_not_empty('email_subject', 'email_subject')
|
||||||
|
append_if_not_empty('email_body', 'email_body')
|
||||||
|
|
||||||
|
aggr.append(entry)
|
||||||
|
|
||||||
|
aggr_text = json.dumps(aggr, indent=2)
|
||||||
|
|
||||||
|
with open('common/src/commonMain/resources/MR/files/justdeleteme.json', 'w') as f:
|
||||||
|
f.write(aggr_text)
|
1
.github/update_justdeleteme.requirements.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
requests
|
35
.github/update_justgetmydata.py
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
URL_JGMD = "https://raw.githubusercontent.com/daviddavo/jgmd/master/_data/sites.json"
|
||||||
|
|
||||||
|
response = requests.get(
|
||||||
|
URL_JGMD,
|
||||||
|
)
|
||||||
|
|
||||||
|
aggr = []
|
||||||
|
|
||||||
|
for site in response.json():
|
||||||
|
entry = {
|
||||||
|
'name': site['name'],
|
||||||
|
'domains': site.get('domains', []),
|
||||||
|
}
|
||||||
|
|
||||||
|
def append_if_not_empty(src_key, dst_key):
|
||||||
|
if src_key in site and site[src_key]:
|
||||||
|
entry[dst_key] = site[src_key]
|
||||||
|
|
||||||
|
append_if_not_empty('url', 'url')
|
||||||
|
append_if_not_empty('difficulty', 'difficulty')
|
||||||
|
append_if_not_empty('notes', 'notes')
|
||||||
|
# for the extra-asshole websites
|
||||||
|
append_if_not_empty('email', 'email')
|
||||||
|
append_if_not_empty('email_subject', 'email_subject')
|
||||||
|
append_if_not_empty('email_body', 'email_body')
|
||||||
|
|
||||||
|
aggr.append(entry)
|
||||||
|
|
||||||
|
aggr_text = json.dumps(aggr, indent=2)
|
||||||
|
|
||||||
|
with open('common/src/commonMain/resources/MR/files/justgetmydata.json', 'w') as f:
|
||||||
|
f.write(aggr_text)
|
1
.github/update_justgetmydata.requirements.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
requests
|
56
.github/update_passkeys.py
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import json
|
||||||
|
import requests
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser("update_passkeys")
|
||||||
|
parser.add_argument("api_key", help="An API Key to access the https://passkeys.directory/ backend.", type=str)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
URL_PASSKEYS = "https://apecbgwekadegtkzpwyh.supabase.co/rest/v1/sites"
|
||||||
|
URL_APIKEY = args.api_key
|
||||||
|
|
||||||
|
response = requests.get(
|
||||||
|
URL_PASSKEYS,
|
||||||
|
params={
|
||||||
|
'select': '*',
|
||||||
|
'or': '(passkey_signin.eq.true,passkey_mfa.eq.true)',
|
||||||
|
},
|
||||||
|
headers={
|
||||||
|
'apikey': URL_APIKEY,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
aggr = []
|
||||||
|
|
||||||
|
for site in response.json():
|
||||||
|
features = []
|
||||||
|
entry = {
|
||||||
|
'name': site['name'],
|
||||||
|
'domain': site['domain'],
|
||||||
|
'features': features,
|
||||||
|
}
|
||||||
|
|
||||||
|
def append_if_not_empty(src_key, dst_key):
|
||||||
|
if src_key in site and site[src_key]:
|
||||||
|
entry[dst_key] = site[src_key]
|
||||||
|
|
||||||
|
append_if_not_empty('documentation_link', 'documentation')
|
||||||
|
append_if_not_empty('setup_link', 'setup')
|
||||||
|
append_if_not_empty('notes', 'notes')
|
||||||
|
|
||||||
|
# convert features to a list
|
||||||
|
if site['passkey_mfa']:
|
||||||
|
features.append('mfa')
|
||||||
|
if site['passkey_signin']:
|
||||||
|
features.append('signin')
|
||||||
|
|
||||||
|
aggr.append(entry)
|
||||||
|
|
||||||
|
# Ensure the file is sorted for better
|
||||||
|
# git diff logs.
|
||||||
|
aggr.sort(key=lambda x: x['domain'])
|
||||||
|
|
||||||
|
aggr_text = json.dumps(aggr, indent=2)
|
||||||
|
|
||||||
|
with open('common/src/commonMain/resources/MR/files/passkeys.json', 'w') as f:
|
||||||
|
f.write(aggr_text)
|
1
.github/update_passkeys.requirements.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
requests
|
49
.github/update_twofactorauth.py
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import re
|
||||||
|
import json
|
||||||
|
|
||||||
|
from io import BytesIO
|
||||||
|
from zipfile import ZipFile
|
||||||
|
from urllib.request import urlopen
|
||||||
|
|
||||||
|
URL_2FA = "https://github.com/2factorauth/twofactorauth/archive/refs/heads/master.zip"
|
||||||
|
|
||||||
|
# Load the zip file without saving it into a file.
|
||||||
|
response = urlopen(URL_2FA)
|
||||||
|
archive = ZipFile(BytesIO(response.read()))
|
||||||
|
|
||||||
|
aggr = []
|
||||||
|
|
||||||
|
# Load each of the json files and save into
|
||||||
|
# a single json array.
|
||||||
|
for file in archive.namelist():
|
||||||
|
if not re.match(r".+/entries/.+\.json", file):
|
||||||
|
continue
|
||||||
|
json_text = archive.read(file).decode('utf-8')
|
||||||
|
json_obj = json.loads(json_text)
|
||||||
|
# Flatten the structure.
|
||||||
|
json_obj_root_key = list(json_obj.keys())[0]
|
||||||
|
json_obj = json_obj[json_obj_root_key]
|
||||||
|
# Add name
|
||||||
|
if not "name" in json_obj:
|
||||||
|
json_obj["name"] = json_obj_root_key
|
||||||
|
# Delete unused attributes.
|
||||||
|
if not "tfa" in json_obj:
|
||||||
|
continue
|
||||||
|
if "img" in json_obj:
|
||||||
|
del json_obj["img"]
|
||||||
|
if "recovery" in json_obj:
|
||||||
|
del json_obj["recovery"]
|
||||||
|
if "keywords" in json_obj:
|
||||||
|
del json_obj["keywords"]
|
||||||
|
if "categories" in json_obj:
|
||||||
|
del json_obj["categories"]
|
||||||
|
if "contact" in json_obj:
|
||||||
|
del json_obj["contact"]
|
||||||
|
if "regions" in json_obj:
|
||||||
|
del json_obj["regions"]
|
||||||
|
aggr.append(json_obj)
|
||||||
|
|
||||||
|
aggr_text = json.dumps(aggr, indent=2)
|
||||||
|
|
||||||
|
with open('common/src/commonMain/resources/MR/files/tfa.json', 'w') as f:
|
||||||
|
f.write(aggr_text)
|
25
.github/workflows/check_gradle_wrapper.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
name: "✔️ Check Gradle wrapper"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- 'gradlew*'
|
||||||
|
- 'gradle/wrapper/**'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- 'gradlew*'
|
||||||
|
- 'gradle/wrapper/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-gradle-wrapper:
|
||||||
|
name: Check Gradle wrapper
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: "Check Gradle wrapper"
|
||||||
|
uses: gradle/wrapper-validation-action@v1
|
36
.github/workflows/check_licenses.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
name: "✔️ Check Licenses"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- '**/*.gradle*'
|
||||||
|
- 'gradle/**'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- '**/*.gradle*'
|
||||||
|
- 'gradle/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-licenses:
|
||||||
|
name: Check Licenses
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: "Set up JDK 17"
|
||||||
|
id: setup-java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: '17'
|
||||||
|
cache: 'gradle'
|
||||||
|
- name: "Check licenses"
|
||||||
|
uses: eskatos/gradle-command-action@v2
|
||||||
|
env:
|
||||||
|
JAVA_HOME: ${{ steps.setup-java.outputs.path }}
|
||||||
|
with:
|
||||||
|
arguments: licensee
|
33
.github/workflows/new_daily_tag.yaml
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
name: "🤖 Daily tag"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
new-tag:
|
||||||
|
name: New tag
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.PERSONAL_TOKEN }}
|
||||||
|
- name: "Create a new daily tag"
|
||||||
|
run: |
|
||||||
|
tag=d"$(date -u +'%Y%m%d')"
|
||||||
|
echo "Name of a new tag is $tag"
|
||||||
|
tag_commit=$(git rev-list -n 1 $(git describe --tags --abbrev=0))
|
||||||
|
echo "$tag -> $tag_commit"
|
||||||
|
master_commit=$(git rev-list -n 1 master)
|
||||||
|
echo "master -> $master_commit"
|
||||||
|
# Create a new tag only if the latest tag does not
|
||||||
|
# point to the same commit as the master branch.
|
||||||
|
if [ "$tag_commit" != "$master_commit" ]; then
|
||||||
|
git config user.email github-actions@github.com
|
||||||
|
git config user.name "${{ github.actor }}"
|
||||||
|
git tag $tag
|
||||||
|
git push origin $tag
|
||||||
|
fi
|
53
.github/workflows/new_daily_tag_play_store_internal_track.yaml
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
name: "🤖 Daily tag -> Play Store internal track"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'd*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-play-store-internal:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: "Set up JDK 17"
|
||||||
|
id: setup-java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: '17'
|
||||||
|
cache: 'gradle'
|
||||||
|
- id: vars
|
||||||
|
run: |
|
||||||
|
echo ::set-output name=tag::${GITHUB_REF:11}
|
||||||
|
- name: "Prepare env"
|
||||||
|
run: |
|
||||||
|
echo ${{ secrets.KEYSTORE_B64 }} | base64 -d | zcat >> androidApp/keyguard-release.keystore
|
||||||
|
echo ${{ secrets.KEYSTORE_PROPS_B64 }} | base64 -d | zcat >> androidApp/keyguard-release.properties
|
||||||
|
echo ${{ secrets.GOOGLE_SERVICES }} | base64 -d | zcat >> androidApp/google-services.json
|
||||||
|
echo ${{ secrets.SERVICE_ACCOUNT_B64 }} | base64 -d | zcat >> service-account-google.json
|
||||||
|
echo "" >> gradle.properties
|
||||||
|
echo versionDate=${{ steps.vars.outputs.tag }} >> gradle.properties
|
||||||
|
echo buildkonfig.flavor=release >> gradle.properties
|
||||||
|
- name: "Check and Build licenses"
|
||||||
|
uses: eskatos/gradle-command-action@v2
|
||||||
|
env:
|
||||||
|
JAVA_HOME: ${{ steps.setup-java.outputs.path }}
|
||||||
|
with:
|
||||||
|
arguments: :androidApp:licenseeAndroidPlayStoreRelease
|
||||||
|
- name: "Move licenses"
|
||||||
|
run: |
|
||||||
|
mv -f androidApp/build/reports/licensee/androidPlayStoreRelease/artifacts.json common/src/commonMain/resources/MR/files/licenses.json
|
||||||
|
- name: "Build release bundle"
|
||||||
|
uses: eskatos/gradle-command-action@v2
|
||||||
|
env:
|
||||||
|
JAVA_HOME: ${{ steps.setup-java.outputs.path }}
|
||||||
|
with:
|
||||||
|
arguments: :androidApp:bundlePlayStoreRelease
|
||||||
|
- name: "Distribute on Play Store"
|
||||||
|
uses: r0adkll/upload-google-play@v1.1.2
|
||||||
|
with:
|
||||||
|
serviceAccountJson: service-account-google.json
|
||||||
|
packageName: com.artemchep.keyguard
|
||||||
|
releaseFiles: androidApp/build/outputs/bundle/playStoreRelease/androidApp-playStore-release.aab
|
||||||
|
track: internal
|
232
.github/workflows/new_tag_release.yaml
vendored
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
name: "🎉 Release tag -> GitHub release"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'r*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-macos-app:
|
||||||
|
runs-on: macos-12
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
|
submodules: recursive
|
||||||
|
- name: "Set up JDK 17"
|
||||||
|
id: setup-java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: '17'
|
||||||
|
cache: 'gradle'
|
||||||
|
- name: "Decode signing certificate"
|
||||||
|
run: |
|
||||||
|
echo ${{ secrets.CERT_B64 }} | base64 -d | zcat > desktopApp/macos-dev.cer
|
||||||
|
- name: "Import signing certificate"
|
||||||
|
uses: apple-actions/import-codesign-certs@v2
|
||||||
|
with:
|
||||||
|
p12-filepath: desktopApp/macos-dev.cer
|
||||||
|
p12-password: ${{ secrets.CERT_PASSWD }}
|
||||||
|
- name: "Setup build env"
|
||||||
|
run: |
|
||||||
|
echo "" >> gradle.properties
|
||||||
|
echo buildkonfig.flavor=release >> gradle.properties
|
||||||
|
- name: "Setup signing config"
|
||||||
|
run: |
|
||||||
|
echo "" >> gradle.properties
|
||||||
|
echo "cert_identity=${{ secrets.CERT_IDENTITY }}" >> gradle.properties
|
||||||
|
- name: "Setup notarization config"
|
||||||
|
run: |
|
||||||
|
echo "" >> gradle.properties
|
||||||
|
echo "notarization_apple_id=${{ secrets.NOTARIZATION_APPLE_ID }}" >> gradle.properties
|
||||||
|
echo "notarization_password=${{ secrets.NOTARIZATION_PASSWD }}" >> gradle.properties
|
||||||
|
echo "notarization_asc_provider=${{ secrets.NOTARIZATION_ASC_PROVIDER }}" >> gradle.properties
|
||||||
|
- name: "./gradlew :desktopApp:packageDmg :desktopApp:notarizeDmg"
|
||||||
|
uses: eskatos/gradle-command-action@v2
|
||||||
|
env:
|
||||||
|
JAVA_HOME: ${{ steps.setup-java.outputs.path }}
|
||||||
|
with:
|
||||||
|
arguments: "-PexcludeAppImage=true :desktopApp:packageDmg :desktopApp:notarizeDmg"
|
||||||
|
- name: 'Upload logs'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: logs-mac
|
||||||
|
path: desktopApp/build/compose/logs/**/*.txt
|
||||||
|
retention-days: 30
|
||||||
|
- name: 'Upload .dmg'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: app-mac
|
||||||
|
path: desktopApp/build/compose/binaries/main/dmg/*.dmg
|
||||||
|
retention-days: 1
|
||||||
|
build-linux-flatpak-app:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: bilelmoussaoui/flatpak-github-actions:gnome-45
|
||||||
|
options: --privileged
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
|
submodules: recursive
|
||||||
|
- name: "Set up JDK 17"
|
||||||
|
id: setup-java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: '17'
|
||||||
|
cache: 'gradle'
|
||||||
|
- name: "Setup build env"
|
||||||
|
run: |
|
||||||
|
echo "" >> gradle.properties
|
||||||
|
echo buildkonfig.flavor=release >> gradle.properties
|
||||||
|
- name: "./gradlew :desktopApp:bundleFlatpak"
|
||||||
|
uses: eskatos/gradle-command-action@v2
|
||||||
|
env:
|
||||||
|
JAVA_HOME: ${{ steps.setup-java.outputs.path }}
|
||||||
|
with:
|
||||||
|
arguments: ":desktopApp:bundleFlatpak"
|
||||||
|
- name: 'Upload .flatpak'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: app-linux-flatpak
|
||||||
|
path: desktopApp/build/flatpak/Keyguard.flatpak
|
||||||
|
retention-days: 1
|
||||||
|
build-windows-app:
|
||||||
|
runs-on: windows-latest
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
|
submodules: recursive
|
||||||
|
- name: "Set up JDK 17"
|
||||||
|
id: setup-java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: |
|
||||||
|
11
|
||||||
|
17
|
||||||
|
cache: 'gradle'
|
||||||
|
- name: "Setup build env"
|
||||||
|
run: |
|
||||||
|
echo "" >> gradle.properties
|
||||||
|
echo buildkonfig.flavor=release >> gradle.properties
|
||||||
|
- name: "./gradlew :desktopApp:packageMsi"
|
||||||
|
uses: eskatos/gradle-command-action@v2
|
||||||
|
env:
|
||||||
|
JAVA_HOME: ${{ steps.setup-java.outputs.path }}
|
||||||
|
with:
|
||||||
|
arguments: ":desktopApp:packageMsi"
|
||||||
|
- name: 'Upload .msi'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: app-windows
|
||||||
|
path: desktopApp/build/compose/binaries/main/msi/*.msi
|
||||||
|
retention-days: 1
|
||||||
|
build-android-app:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: "Set up JDK 17"
|
||||||
|
id: setup-java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: '17'
|
||||||
|
cache: 'gradle'
|
||||||
|
- id: vars
|
||||||
|
run: |
|
||||||
|
echo ::set-output name=tag::${GITHUB_REF:11}
|
||||||
|
- name: "Prepare env"
|
||||||
|
run: |
|
||||||
|
echo ${{ secrets.KEYSTORE_B64 }} | base64 -d | zcat >> androidApp/keyguard-release.keystore
|
||||||
|
echo ${{ secrets.KEYSTORE_PROPS_B64 }} | base64 -d | zcat >> androidApp/keyguard-release.properties
|
||||||
|
echo ${{ secrets.GOOGLE_SERVICES }} | base64 -d | zcat >> androidApp/google-services.json
|
||||||
|
echo "" >> gradle.properties
|
||||||
|
echo versionDate=${{ steps.vars.outputs.tag }} >> gradle.properties
|
||||||
|
echo buildkonfig.flavor=release >> gradle.properties
|
||||||
|
- name: "Check and Build licenses"
|
||||||
|
uses: eskatos/gradle-command-action@v2
|
||||||
|
env:
|
||||||
|
JAVA_HOME: ${{ steps.setup-java.outputs.path }}
|
||||||
|
with:
|
||||||
|
arguments: :androidApp:licenseeAndroidNoneRelease
|
||||||
|
- name: "Move licenses"
|
||||||
|
run: |
|
||||||
|
mv -f androidApp/build/reports/licensee/androidNoneRelease/artifacts.json common/src/commonMain/resources/MR/files/licenses.json
|
||||||
|
- name: "./gradlew :androidApp:assembleNoneRelease"
|
||||||
|
uses: eskatos/gradle-command-action@v2
|
||||||
|
env:
|
||||||
|
JAVA_HOME: ${{ steps.setup-java.outputs.path }}
|
||||||
|
with:
|
||||||
|
arguments: :androidApp:assembleNoneRelease
|
||||||
|
- name: 'Upload .apk'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: app-android
|
||||||
|
path: |
|
||||||
|
androidApp/build/outputs/apk/**/*.apk
|
||||||
|
androidApp/build/outputs/mapping/**/mapping.txt
|
||||||
|
retention-days: 1
|
||||||
|
dist:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- build-android-app
|
||||||
|
- build-linux-flatpak-app
|
||||||
|
- build-macos-app
|
||||||
|
- build-windows-app
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: "Generate changelog"
|
||||||
|
id: changelog
|
||||||
|
uses: metcalfc/changelog-generator@v4.2.0
|
||||||
|
with:
|
||||||
|
myToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: "Download Mac app"
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: app-mac
|
||||||
|
path: artifacts
|
||||||
|
- name: "Download Linux app"
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: app-linux-flatpak
|
||||||
|
path: artifacts
|
||||||
|
- name: "Download Windows app"
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: app-windows
|
||||||
|
path: artifacts
|
||||||
|
- name: "Download Android app"
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: app-android
|
||||||
|
path: artifacts
|
||||||
|
- name: "Create release"
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
with:
|
||||||
|
name: Release ${{ github.ref }}
|
||||||
|
body: ${{ steps.changelog.outputs.changelog }}
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
files: |
|
||||||
|
artifacts/*
|
50
.github/workflows/update_gpm_passkeys_priv_apps.yml
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
name: "🕒 Synchronize GPM Credential Privileged Apps"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
# Configuration.
|
||||||
|
- '.github/update_gpm_passkeys_priv_apps.py'
|
||||||
|
- '.github/update_gpm_passkeys_priv_apps.requirements.txt'
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 5'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-gpm-passkeys-priv-apps:
|
||||||
|
name: Synchronize GPM Credential Privileged Apps
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: pip install -r .github/update_gpm_passkeys_priv_apps.requirements.txt
|
||||||
|
- name: "Update library"
|
||||||
|
run: |
|
||||||
|
python .github/update_gpm_passkeys_priv_apps.py
|
||||||
|
- name: "Check if any changes"
|
||||||
|
id: check-changes
|
||||||
|
run: |
|
||||||
|
has_changes=$(if [ -n "$(git status --porcelain)" ]; then echo "true"; else echo "false"; fi)
|
||||||
|
echo "$has_changes"
|
||||||
|
echo "{HAS_CHANGES}={$has_changes}" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: Commit and push changes
|
||||||
|
uses: devops-infra/action-commit-push@v0.9.2
|
||||||
|
if: ${{ startsWith(steps.check-changes.outputs.HAS_CHANGES, 'true') }}
|
||||||
|
with:
|
||||||
|
github_token: "${{ secrets.PERSONAL_TOKEN }}"
|
||||||
|
add_timestamp: false
|
||||||
|
commit_prefix: "[AUTO]"
|
||||||
|
commit_message: "Update GPM Credential Privileged Apps JSON"
|
||||||
|
force: true
|
||||||
|
target_branch: gpmpasskeysprivapps_action
|
||||||
|
- name: Create pull request
|
||||||
|
uses: devops-infra/action-pull-request@v0.5.5
|
||||||
|
if: ${{ startsWith(steps.check-changes.outputs.HAS_CHANGES, 'true') }}
|
||||||
|
with:
|
||||||
|
github_token: "${{ secrets.PERSONAL_TOKEN }}"
|
||||||
|
source_branch: gpmpasskeysprivapps_action
|
||||||
|
target_branch: master
|
||||||
|
assignee: AChep
|
||||||
|
label: "robot,enhancement"
|
||||||
|
title: New GPM Credential Privileged Apps by GitHub Action
|
50
.github/workflows/update_justdeleteme.yml
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
name: "🕒 Synchronize Just delete me"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
# Configuration.
|
||||||
|
- '.github/update_justdeleteme.py'
|
||||||
|
- '.github/update_justdeleteme.requirements.txt'
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 5'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-passkeys:
|
||||||
|
name: Synchronize Just delete me
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: pip install -r .github/update_justdeleteme.requirements.txt
|
||||||
|
- name: "Update library"
|
||||||
|
run: |
|
||||||
|
python .github/update_justdeleteme.py
|
||||||
|
- name: "Check if any changes"
|
||||||
|
id: check-changes
|
||||||
|
run: |
|
||||||
|
has_changes=$(if [ -n "$(git status --porcelain)" ]; then echo "true"; else echo "false"; fi)
|
||||||
|
echo "$has_changes"
|
||||||
|
echo "{HAS_CHANGES}={$has_changes}" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: Commit and push changes
|
||||||
|
uses: devops-infra/action-commit-push@v0.9.2
|
||||||
|
if: ${{ startsWith(steps.check-changes.outputs.HAS_CHANGES, 'true') }}
|
||||||
|
with:
|
||||||
|
github_token: "${{ secrets.PERSONAL_TOKEN }}"
|
||||||
|
add_timestamp: false
|
||||||
|
commit_prefix: "[AUTO]"
|
||||||
|
commit_message: "Update justdeleteme library"
|
||||||
|
force: true
|
||||||
|
target_branch: justdeleteme_action
|
||||||
|
- name: Create pull request
|
||||||
|
uses: devops-infra/action-pull-request@v0.5.5
|
||||||
|
if: ${{ startsWith(steps.check-changes.outputs.HAS_CHANGES, 'true') }}
|
||||||
|
with:
|
||||||
|
github_token: "${{ secrets.PERSONAL_TOKEN }}"
|
||||||
|
source_branch: justdeleteme_action
|
||||||
|
target_branch: master
|
||||||
|
assignee: AChep
|
||||||
|
label: "robot,enhancement"
|
||||||
|
title: New Just delete me by GitHub Action
|
50
.github/workflows/update_justgetmydata.yml
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
name: "🕒 Synchronize Just get my data"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
# Configuration.
|
||||||
|
- '.github/update_justgetmydata.py'
|
||||||
|
- '.github/update_justgetmydata.requirements.txt'
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 5'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-passkeys:
|
||||||
|
name: Synchronize Just get my data
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: pip install -r .github/update_justgetmydata.requirements.txt
|
||||||
|
- name: "Update library"
|
||||||
|
run: |
|
||||||
|
python .github/update_justgetmydata.py
|
||||||
|
- name: "Check if any changes"
|
||||||
|
id: check-changes
|
||||||
|
run: |
|
||||||
|
has_changes=$(if [ -n "$(git status --porcelain)" ]; then echo "true"; else echo "false"; fi)
|
||||||
|
echo "$has_changes"
|
||||||
|
echo "{HAS_CHANGES}={$has_changes}" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: Commit and push changes
|
||||||
|
uses: devops-infra/action-commit-push@v0.9.2
|
||||||
|
if: ${{ startsWith(steps.check-changes.outputs.HAS_CHANGES, 'true') }}
|
||||||
|
with:
|
||||||
|
github_token: "${{ secrets.PERSONAL_TOKEN }}"
|
||||||
|
add_timestamp: false
|
||||||
|
commit_prefix: "[AUTO]"
|
||||||
|
commit_message: "Update justgetmydata library"
|
||||||
|
force: true
|
||||||
|
target_branch: justgetmydata_action
|
||||||
|
- name: Create pull request
|
||||||
|
uses: devops-infra/action-pull-request@v0.5.5
|
||||||
|
if: ${{ startsWith(steps.check-changes.outputs.HAS_CHANGES, 'true') }}
|
||||||
|
with:
|
||||||
|
github_token: "${{ secrets.PERSONAL_TOKEN }}"
|
||||||
|
source_branch: justgetmydata_action
|
||||||
|
target_branch: master
|
||||||
|
assignee: AChep
|
||||||
|
label: "robot,enhancement"
|
||||||
|
title: New Just get my data by GitHub Action
|
26
.github/workflows/update_listing_google.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
name: "🌐 Upload Google Play Store listing"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- 'listing/google/**'
|
||||||
|
# Configuration.
|
||||||
|
- '.github/update_google_play_listing.py'
|
||||||
|
- '.github/update_google_play_listing.requirements.py'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-google-listing:
|
||||||
|
name: Upload Google Play Store listing
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: pip install -r .github/update_google_play_listing.requirements.txt
|
||||||
|
- name: "Setup secrets"
|
||||||
|
run: |
|
||||||
|
echo ${{ secrets.SERVICE_ACCOUNT_B64 }} | base64 -d | zcat >> service-account-google.json
|
||||||
|
- name: "Update listing"
|
||||||
|
run: |
|
||||||
|
python .github/update_google_play_listing.py
|
35
.github/workflows/update_localization.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
name: "🌐 Synchronize Localization"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- 'common/src/commonMain/resources/MR/base/*.xml'
|
||||||
|
- 'listing/google/base/*.html'
|
||||||
|
- 'listing/google/base/*.xml'
|
||||||
|
# Configuration.
|
||||||
|
- 'crowdin.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-localization:
|
||||||
|
name: Synchronize Crowdin Translations
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: crowdin/github-action@v1
|
||||||
|
with:
|
||||||
|
upload_sources: true
|
||||||
|
# We only want to upload the sources, nothing else is
|
||||||
|
# supported.
|
||||||
|
upload_translations: false
|
||||||
|
download_translations: true
|
||||||
|
create_pull_request: true
|
||||||
|
pull_request_labels: 'robot,enhancement'
|
||||||
|
pull_request_assignees: 'AChep'
|
||||||
|
config: 'crowdin.yml'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
|
||||||
|
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||||
|
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
50
.github/workflows/update_passkeys.yml
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
name: "🕒 Synchronize Passkeys"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
# Configuration.
|
||||||
|
- '.github/update_passkeys.py'
|
||||||
|
- '.github/update_passkeys.requirements.txt'
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 5'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-passkeys:
|
||||||
|
name: Synchronize Passkeys
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: pip install -r .github/update_passkeys.requirements.txt
|
||||||
|
- name: "Update library"
|
||||||
|
run: |
|
||||||
|
python .github/update_passkeys.py "${{ secrets.PASSKEYS_API_KEY }}"
|
||||||
|
- name: "Check if any changes"
|
||||||
|
id: check-changes
|
||||||
|
run: |
|
||||||
|
has_changes=$(if [ -n "$(git status --porcelain)" ]; then echo "true"; else echo "false"; fi)
|
||||||
|
echo "$has_changes"
|
||||||
|
echo "{HAS_CHANGES}={$has_changes}" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: Commit and push changes
|
||||||
|
uses: devops-infra/action-commit-push@v0.9.2
|
||||||
|
if: ${{ startsWith(steps.check-changes.outputs.HAS_CHANGES, 'true') }}
|
||||||
|
with:
|
||||||
|
github_token: "${{ secrets.PERSONAL_TOKEN }}"
|
||||||
|
add_timestamp: false
|
||||||
|
commit_prefix: "[AUTO]"
|
||||||
|
commit_message: "Update passkeys library"
|
||||||
|
force: true
|
||||||
|
target_branch: passkeys_action
|
||||||
|
- name: Create pull request
|
||||||
|
uses: devops-infra/action-pull-request@v0.5.5
|
||||||
|
if: ${{ startsWith(steps.check-changes.outputs.HAS_CHANGES, 'true') }}
|
||||||
|
with:
|
||||||
|
github_token: "${{ secrets.PERSONAL_TOKEN }}"
|
||||||
|
source_branch: passkeys_action
|
||||||
|
target_branch: master
|
||||||
|
assignee: AChep
|
||||||
|
label: "robot,enhancement"
|
||||||
|
title: New Passkeys by GitHub Action
|
43
.github/workflows/update_tld.yml
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
name: "🕒 Synchronize TLD public suffix list"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 5'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-tld:
|
||||||
|
name: Synchronize TLD public suffix list
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: "Update data"
|
||||||
|
run: |
|
||||||
|
wget https://publicsuffix.org/list/public_suffix_list.dat
|
||||||
|
mv -f public_suffix_list.dat common/src/commonMain/resources/MR/files/public_suffix_list.txt
|
||||||
|
- name: "Check if any changes"
|
||||||
|
id: check-changes
|
||||||
|
run: |
|
||||||
|
has_changes=$(if [ -n "$(git status --porcelain)" ]; then echo "true"; else echo "false"; fi)
|
||||||
|
echo "$has_changes"
|
||||||
|
echo "{HAS_CHANGES}={$has_changes}" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: Commit and push changes
|
||||||
|
uses: devops-infra/action-commit-push@v0.9.2
|
||||||
|
if: ${{ startsWith(steps.check-changes.outputs.HAS_CHANGES, 'true') }}
|
||||||
|
with:
|
||||||
|
github_token: "${{ secrets.PERSONAL_TOKEN }}"
|
||||||
|
add_timestamp: false
|
||||||
|
commit_prefix: "[AUTO]"
|
||||||
|
commit_message: "Update Public suffix list"
|
||||||
|
force: true
|
||||||
|
target_branch: tld_public_suffix_list_action
|
||||||
|
- name: Create pull request
|
||||||
|
uses: devops-infra/action-pull-request@v0.5.5
|
||||||
|
if: ${{ startsWith(steps.check-changes.outputs.HAS_CHANGES, 'true') }}
|
||||||
|
with:
|
||||||
|
github_token: "${{ secrets.PERSONAL_TOKEN }}"
|
||||||
|
source_branch: tld_public_suffix_list_action
|
||||||
|
target_branch: master
|
||||||
|
assignee: AChep
|
||||||
|
label: "robot,enhancement"
|
||||||
|
title: New Public suffix list by GitHub Action
|
48
.github/workflows/update_twofactorauth.yml
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
name: "🕒 Synchronize Two-factor auth"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
# Configuration.
|
||||||
|
- '.github/update_twofactorauth.py'
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 5'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-2fa:
|
||||||
|
name: Synchronize Two-factor authentication
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: "Update library"
|
||||||
|
run: |
|
||||||
|
python .github/update_twofactorauth.py
|
||||||
|
- name: "Check if any changes"
|
||||||
|
id: check-changes
|
||||||
|
run: |
|
||||||
|
has_changes=$(if [ -n "$(git status --porcelain)" ]; then echo "true"; else echo "false"; fi)
|
||||||
|
echo "$has_changes"
|
||||||
|
echo "{HAS_CHANGES}={$has_changes}" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: Commit and push changes
|
||||||
|
uses: devops-infra/action-commit-push@v0.9.2
|
||||||
|
if: ${{ startsWith(steps.check-changes.outputs.HAS_CHANGES, 'true') }}
|
||||||
|
with:
|
||||||
|
github_token: "${{ secrets.PERSONAL_TOKEN }}"
|
||||||
|
add_timestamp: false
|
||||||
|
commit_prefix: "[AUTO]"
|
||||||
|
commit_message: "Update two-factor auth library"
|
||||||
|
force: true
|
||||||
|
target_branch: tfa_2factorauth_action
|
||||||
|
- name: Create pull request
|
||||||
|
uses: devops-infra/action-pull-request@v0.5.5
|
||||||
|
if: ${{ startsWith(steps.check-changes.outputs.HAS_CHANGES, 'true') }}
|
||||||
|
with:
|
||||||
|
github_token: "${{ secrets.PERSONAL_TOKEN }}"
|
||||||
|
source_branch: tfa_2factorauth_action
|
||||||
|
target_branch: master
|
||||||
|
assignee: AChep
|
||||||
|
label: "robot,enhancement"
|
||||||
|
title: New Two-factor auth by GitHub Action
|
61
.gitignore
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
*.DS_Store
|
||||||
|
|
||||||
|
# Built application files
|
||||||
|
*.apk
|
||||||
|
*.ap_
|
||||||
|
|
||||||
|
# Files for the ART/Dalvik VM
|
||||||
|
*.dex
|
||||||
|
|
||||||
|
# Java class files
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
bin/
|
||||||
|
gen/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# Gradle files
|
||||||
|
.gradle/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Local configuration file (sdk path, etc)
|
||||||
|
local.properties
|
||||||
|
|
||||||
|
# Proguard folder generated by Eclipse
|
||||||
|
proguard/
|
||||||
|
|
||||||
|
# Log Files
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Android Studio Navigation editor temp files
|
||||||
|
.navigation/
|
||||||
|
|
||||||
|
# Android Studio captures folder
|
||||||
|
captures/
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
*.iml
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Keystore files
|
||||||
|
# Uncomment the following line if you do not want to check your keystore files in.
|
||||||
|
#*.jks
|
||||||
|
|
||||||
|
# External native build folder generated in Android Studio 2.2 and later
|
||||||
|
.externalNativeBuild
|
||||||
|
|
||||||
|
# Google Services (e.g. APIs or Firebase)
|
||||||
|
google-services.json
|
||||||
|
|
||||||
|
# Freeline
|
||||||
|
freeline.py
|
||||||
|
freeline/
|
||||||
|
freeline_project_description.json
|
||||||
|
|
||||||
|
# fastlane
|
||||||
|
fastlane/report.xml
|
||||||
|
fastlane/Preview.html
|
||||||
|
fastlane/screenshots
|
||||||
|
fastlane/test_output
|
||||||
|
fastlane/readme.md
|
13
README.md
@ -14,8 +14,19 @@ _Can be used with any Bitwarden® installation. This product is not associated w
|
|||||||
#### Highlights:
|
#### Highlights:
|
||||||
- a beautiful rich **Material You** user interface;
|
- a beautiful rich **Material You** user interface;
|
||||||
- a **powerful** and **fast search**;
|
- a **powerful** and **fast search**;
|
||||||
- a watchtower that finds items with **Pwned passwords**, **Vulnerable accounts**, **Reused passwords**, **Inactive two factor authentication**, **Inactive passkeys**, **Unsecure Websites** as well as **Duplicate**, **Incomplete** and **Expiring** items, and other;
|
- a support for creating & using **passkeys** - a modern alternative to passwords.
|
||||||
|
- a watchtower that finds items with **Pwned passwords**, **Vulnerable accounts**, **Reused passwords**, **Inactive two factor authentication**, **Inactive passkeys**, **Unsecure Websites** as well as **Duplicate**, **Incomplete** and **Expiring** items, and other;
|
||||||
- **multi-account support** with secure login and two-factor authentication support;
|
- **multi-account support** with secure login and two-factor authentication support;
|
||||||
- add items, modify, and view your vault **offline**.
|
- add items, modify, and view your vault **offline**.
|
||||||
- beautiful **Light**/**Dark theme**;
|
- beautiful **Light**/**Dark theme**;
|
||||||
- and much more!
|
- and much more!
|
||||||
|
|
||||||
|
#### Looks:
|
||||||
|
| | | |
|
||||||
|
| :----: | :----: | :----: |
|
||||||
|
| ![1](https://github.com/AChep/keyguard-app/blob/master/screenshots/phone/Screenshot_20230928_233006.png) | ![2](https://github.com/AChep/keyguard-app/blob/master/screenshots/phone/Screenshot_20230928_233040.png) | ![3](https://github.com/AChep/keyguard-app/blob/master/screenshots/phone/Screenshot_20230928_233118.png) |
|
||||||
|
| ![4](https://github.com/AChep/keyguard-app/blob/master/screenshots/phone/Screenshot_20230928_233159.png) | ![5](https://github.com/AChep/keyguard-app/blob/master/screenshots/phone/Screenshot_20230928_233236.png) | ![6](https://github.com/AChep/keyguard-app/blob/master/screenshots/phone/Screenshot_20230928_233342.png) |
|
||||||
|
|
||||||
|
## License:
|
||||||
|
|
||||||
|
The source code is available for **personal use** only.
|
||||||
|
7
androidApp/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/build
|
||||||
|
|
||||||
|
# Ignore production keys from being
|
||||||
|
# include into the source system.
|
||||||
|
keyguard-release*
|
||||||
|
# Ignore google-services.json
|
||||||
|
google-services.json
|
2
androidApp/benchmark-rules.pro
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Disables obfuscation for benchmark builds.
|
||||||
|
-dontobfuscate
|
158
androidApp/build.gradle.kts
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import java.io.FileInputStream
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application)
|
||||||
|
alias(libs.plugins.kotlin.android)
|
||||||
|
alias(libs.plugins.kotlin.kapt)
|
||||||
|
alias(libs.plugins.kotlin.plugin.parcelize)
|
||||||
|
alias(libs.plugins.kotlin.plugin.serialization)
|
||||||
|
alias(libs.plugins.compose)
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
|
alias(libs.plugins.ktlint)
|
||||||
|
alias(libs.plugins.google.services)
|
||||||
|
alias(libs.plugins.crashlytics)
|
||||||
|
alias(libs.plugins.baseline.profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadProps(fileName: String): Properties {
|
||||||
|
val props = Properties()
|
||||||
|
val file = file(fileName)
|
||||||
|
if (file.exists()) {
|
||||||
|
var stream: FileInputStream? = null
|
||||||
|
try {
|
||||||
|
stream = file.inputStream()
|
||||||
|
props.load(stream)
|
||||||
|
} finally {
|
||||||
|
stream?.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props
|
||||||
|
}
|
||||||
|
|
||||||
|
val versionInfo = createVersionInfo(
|
||||||
|
marketingVersion = libs.versions.appVersionName.get(),
|
||||||
|
logicalVersion = libs.versions.appVersionCode.get().toInt(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val qaSigningProps = loadProps("keyguard-qa.properties")
|
||||||
|
val releaseSigningProps = loadProps("keyguard-release.properties")
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdk = libs.versions.androidCompileSdk.get().toInt()
|
||||||
|
namespace = "com.artemchep.keyguard"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "com.artemchep.keyguard"
|
||||||
|
minSdk = libs.versions.androidMinSdk.get().toInt()
|
||||||
|
targetSdk = libs.versions.androidTargetSdk.get().toInt()
|
||||||
|
|
||||||
|
versionCode = versionInfo.logicalVersion
|
||||||
|
versionName = versionInfo.marketingVersion
|
||||||
|
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
vectorDrawables {
|
||||||
|
useSupportLibrary = true
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
freeCompilerArgs += listOf(
|
||||||
|
"-opt-in=androidx.compose.material.ExperimentalMaterialApi",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
isCoreLibraryDesugaringEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
androidResources {
|
||||||
|
@Suppress("UnstableApiUsage")
|
||||||
|
generateLocaleConfig = true
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle {
|
||||||
|
language {
|
||||||
|
enableSplit = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// previous-compilation-data.bin is Gradle's internal machinery for the incremental compilation
|
||||||
|
// that seemed to be packed into the resulting artifact because the lib is depending directly
|
||||||
|
// on the compilation task's output for JPMS/Multi-Release JAR support.
|
||||||
|
//
|
||||||
|
// > A failure occurred while executing com.android.build.gradle.internal.tasks.MergeJavaResWorkAction
|
||||||
|
// > 2 files found with path 'META-INF/versions/9/previous-compilation-data.bin' from inputs:
|
||||||
|
// - /home/runner/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-datetime-jvm/0.4.1/684eec210b21e2da7382a4aa85e56fb7b71f39b3/kotlinx-datetime-jvm-0.4.1.jar
|
||||||
|
// - /home/runner/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/atomicfu-jvm/0.22.0/c6a128a44ba52a18265e5ec816130cd341d80792/atomicfu-jvm-0.22.0.jar
|
||||||
|
packagingOptions {
|
||||||
|
resources.excludes.add("META-INF/versions/9/previous-compilation-data.bin")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
buildConfig = true
|
||||||
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
maybeCreate("debug").apply {
|
||||||
|
keyAlias = qaSigningProps.getProperty("key_alias")
|
||||||
|
keyPassword = qaSigningProps.getProperty("password_store")
|
||||||
|
storeFile = file("keyguard-qa.keystore")
|
||||||
|
storePassword = qaSigningProps.getProperty("password_key")
|
||||||
|
}
|
||||||
|
maybeCreate("release").apply {
|
||||||
|
keyAlias = releaseSigningProps.getProperty("key_alias")
|
||||||
|
keyPassword = releaseSigningProps.getProperty("password_store")
|
||||||
|
storeFile = file("keyguard-release.keystore")
|
||||||
|
storePassword = releaseSigningProps.getProperty("password_key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
debug {
|
||||||
|
applicationIdSuffix = ".debug"
|
||||||
|
}
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = true
|
||||||
|
signingConfig = signingConfigs.getByName("release")
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
maybeCreate("benchmark").apply {
|
||||||
|
initWith(getByName("release"))
|
||||||
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
|
// Selects release buildType if the 'benchmark'
|
||||||
|
// buildType not available in other modules.
|
||||||
|
matchingFallbacks += "release"
|
||||||
|
// Do not obfuscate
|
||||||
|
proguardFiles(
|
||||||
|
"benchmark-rules.pro",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val accountManagementDimension = "accountManagement"
|
||||||
|
flavorDimensions += accountManagementDimension
|
||||||
|
productFlavors {
|
||||||
|
maybeCreate("playStore").apply {
|
||||||
|
dimension = accountManagementDimension
|
||||||
|
buildConfigField("boolean", "ANALYTICS", "true")
|
||||||
|
}
|
||||||
|
maybeCreate("none").apply {
|
||||||
|
dimension = accountManagementDimension
|
||||||
|
buildConfigField("boolean", "ANALYTICS", "false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":common"))
|
||||||
|
baselineProfile(project(":androidBenchmark"))
|
||||||
|
coreLibraryDesugaring(libs.android.desugarjdklibs)
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(libs.versions.jdk.get().toInt())
|
||||||
|
}
|
BIN
androidApp/keyguard-qa.keystore
Normal file
3
androidApp/keyguard-qa.properties
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
key_alias=AChep
|
||||||
|
password_store=qqQJQ32ENxi9@ffMVx9crTgQ
|
||||||
|
password_key=qqQJQ32ENxi9@ffMVx9crTgQ
|
115
androidApp/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
||||||
|
##
|
||||||
|
## https://github.com/sqlcipher/sqlcipher-android/issues/18
|
||||||
|
##
|
||||||
|
|
||||||
|
-keep class androidx.room.** extends androidx.sqlite.db.SupportSQLiteOpenHelper
|
||||||
|
|
||||||
|
-keep,includedescriptorclasses class net.zetetic.database.** { *; }
|
||||||
|
-keep,includedescriptorclasses interface net.zetetic.database.** { *; }
|
||||||
|
|
||||||
|
##
|
||||||
|
## https://github.com/Kotlin/kotlinx.serialization#android
|
||||||
|
##
|
||||||
|
|
||||||
|
# Keep `Companion` object fields of serializable classes.
|
||||||
|
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
|
||||||
|
-if @kotlinx.serialization.Serializable class **
|
||||||
|
-keepclassmembers class <1> {
|
||||||
|
static <1>$Companion Companion;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Keep `serializer()` on companion objects (both default and named) of serializable classes.
|
||||||
|
-if @kotlinx.serialization.Serializable class ** {
|
||||||
|
static **$* *;
|
||||||
|
}
|
||||||
|
-keepclassmembers class <2>$<3> {
|
||||||
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Keep `INSTANCE.serializer()` of serializable objects.
|
||||||
|
-if @kotlinx.serialization.Serializable class ** {
|
||||||
|
public static ** INSTANCE;
|
||||||
|
}
|
||||||
|
-keepclassmembers class <1> {
|
||||||
|
public static <1> INSTANCE;
|
||||||
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
|
||||||
|
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
|
||||||
|
|
||||||
|
# Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`.
|
||||||
|
# If you have any, uncomment and replace classes with those containing named companion objects.
|
||||||
|
#-keepattributes InnerClasses # Needed for `getDeclaredClasses`.
|
||||||
|
#-if @kotlinx.serialization.Serializable class
|
||||||
|
#com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions.
|
||||||
|
#com.example.myapplication.HasNamedCompanion2
|
||||||
|
#{
|
||||||
|
# static **$* *;
|
||||||
|
#}
|
||||||
|
#-keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept.
|
||||||
|
# static <1>$$serializer INSTANCE;
|
||||||
|
#}
|
||||||
|
|
||||||
|
##
|
||||||
|
## kodein
|
||||||
|
##
|
||||||
|
|
||||||
|
-keep, allowobfuscation, allowoptimization class org.kodein.type.TypeReference
|
||||||
|
-keep, allowobfuscation, allowoptimization class org.kodein.type.JVMAbstractTypeToken$Companion$WrappingTest
|
||||||
|
|
||||||
|
-keep, allowobfuscation, allowoptimization class * extends org.kodein.type.TypeReference
|
||||||
|
-keep, allowobfuscation, allowoptimization class * extends org.kodein.type.JVMAbstractTypeToken$Companion$WrappingTest
|
||||||
|
|
||||||
|
##
|
||||||
|
## java rx
|
||||||
|
##
|
||||||
|
|
||||||
|
# https://github.com/ReactiveX/RxJava#r8-and-proguard-settings
|
||||||
|
-dontwarn java.util.concurrent.Flow*
|
||||||
|
|
||||||
|
##
|
||||||
|
## signalr
|
||||||
|
##
|
||||||
|
|
||||||
|
-keep class com.microsoft.signalr.** { *; }
|
||||||
|
-keep interface com.microsoft.signalr.** { *; }
|
||||||
|
|
||||||
|
##
|
||||||
|
## messagepack
|
||||||
|
##
|
||||||
|
|
||||||
|
-keep class org.msgpack.core.buffer.** { *; }
|
||||||
|
|
||||||
|
##
|
||||||
|
## dont warn
|
||||||
|
##
|
||||||
|
|
||||||
|
-dontwarn edu.umd.cs.findbugs.annotations.**
|
||||||
|
-dontwarn java.sql.JDBCType
|
||||||
|
#-dontwarn okhttp3.internal.platform.**
|
||||||
|
#-dontwarn org.conscrypt.**
|
||||||
|
#-dontwarn org.bouncycastle.jsse**
|
||||||
|
#-dontwarn org.openjsse.**
|
3
androidApp/src/debug/res/values/strings.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">KeyDev</string>
|
||||||
|
</resources>
|
44
androidApp/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:installLocation="internalOnly">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Disallow backup.
|
||||||
|
|
||||||
|
Most of the data is encrypted with android device key, so if you
|
||||||
|
restore a backup you can not decrypt it.
|
||||||
|
-->
|
||||||
|
<application
|
||||||
|
android:name=".Main"
|
||||||
|
android:enableOnBackInvokedCallback="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:networkSecurityConfig="@xml/network_configuration"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.Keyguard.Splash">
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.fileProvider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/provider_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
|
<!-- https://developer.android.com/guide/topics/resources/app-languages#android12-impl -->
|
||||||
|
<service
|
||||||
|
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
||||||
|
android:enabled="false"
|
||||||
|
android:exported="false">
|
||||||
|
<meta-data
|
||||||
|
android:name="autoStoreLocales"
|
||||||
|
android:value="true" />
|
||||||
|
</service>
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
14
androidApp/src/main/assets/logback.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<configuration>
|
||||||
|
<appender name="logcat" class="ch.qos.logback.classic.android.LogcatAppender">
|
||||||
|
<tagEncoder>
|
||||||
|
<pattern>%logger{12}</pattern>
|
||||||
|
</tagEncoder>
|
||||||
|
<encoder>
|
||||||
|
<pattern>[%-20thread] %msg</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root level="DEBUG">
|
||||||
|
<appender-ref ref="logcat" />
|
||||||
|
</root>
|
||||||
|
</configuration>
|
227
androidApp/src/main/java/com/artemchep/keyguard/Main.kt
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
package com.artemchep.keyguard
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.artemchep.bindin.bindBlock
|
||||||
|
import com.artemchep.keyguard.android.BaseApp
|
||||||
|
import com.artemchep.keyguard.android.downloader.journal.DownloadRepository
|
||||||
|
import com.artemchep.keyguard.android.downloader.worker.AttachmentDownloadAllWorker
|
||||||
|
import com.artemchep.keyguard.android.passkeysModule
|
||||||
|
import com.artemchep.keyguard.billing.BillingManager
|
||||||
|
import com.artemchep.keyguard.billing.BillingManagerImpl
|
||||||
|
import com.artemchep.keyguard.common.AppWorker
|
||||||
|
import com.artemchep.keyguard.common.io.*
|
||||||
|
import com.artemchep.keyguard.common.model.Screen
|
||||||
|
import com.artemchep.keyguard.common.service.logging.LogRepository
|
||||||
|
import com.artemchep.keyguard.common.service.power.PowerService
|
||||||
|
import com.artemchep.keyguard.common.usecase.*
|
||||||
|
import com.artemchep.keyguard.common.usecase.impl.CleanUpAttachmentImpl
|
||||||
|
import com.artemchep.keyguard.core.session.diFingerprintRepositoryModule
|
||||||
|
import com.artemchep.keyguard.common.model.MasterSession
|
||||||
|
import com.artemchep.keyguard.common.service.vault.KeyReadWriteRepository
|
||||||
|
import com.artemchep.keyguard.common.model.PersistedSession
|
||||||
|
import com.artemchep.keyguard.feature.favicon.Favicon
|
||||||
|
import com.artemchep.keyguard.feature.localization.textResource
|
||||||
|
import com.artemchep.keyguard.platform.LeContext
|
||||||
|
import com.artemchep.keyguard.platform.lifecycle.toCommon
|
||||||
|
import com.artemchep.keyguard.res.Res
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import kotlinx.datetime.Clock
|
||||||
|
import org.kodein.di.*
|
||||||
|
import org.kodein.di.android.x.androidXModule
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class Main : BaseApp(), DIAware {
|
||||||
|
override val di by DI.lazy {
|
||||||
|
import(androidXModule(this@Main))
|
||||||
|
import(diFingerprintRepositoryModule())
|
||||||
|
import(passkeysModule())
|
||||||
|
bind<BillingManager>() with singleton {
|
||||||
|
BillingManagerImpl(
|
||||||
|
context = this@Main,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See:
|
||||||
|
// https://issuetracker.google.com/issues/243457462
|
||||||
|
override fun attachBaseContext(base: Context) {
|
||||||
|
val updatedContext = ContextCompat.getContextForLanguage(base)
|
||||||
|
|
||||||
|
// Update locale only if needed.
|
||||||
|
val updatedLocale: Locale =
|
||||||
|
updatedContext.resources.configuration.locale
|
||||||
|
if (!Locale.getDefault().equals(updatedLocale)) {
|
||||||
|
Locale.setDefault(updatedLocale)
|
||||||
|
}
|
||||||
|
super.attachBaseContext(updatedContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
val logRepository: LogRepository by instance()
|
||||||
|
val getVaultSession: GetVaultSession by instance()
|
||||||
|
val putVaultSession: PutVaultSession by instance()
|
||||||
|
val getVaultPersist: GetVaultPersist by instance()
|
||||||
|
val keyReadWriteRepository: KeyReadWriteRepository by instance()
|
||||||
|
val clearVaultSession: ClearVaultSession by instance()
|
||||||
|
val downloadRepository: DownloadRepository by instance()
|
||||||
|
val cleanUpAttachment: CleanUpAttachment by instance()
|
||||||
|
val appWorker: AppWorker by instance(tag = AppWorker.Feature.SYNC)
|
||||||
|
// val cleanUpDownload: CleanUpDownloadImpl by instance()
|
||||||
|
|
||||||
|
val processLifecycleOwner = ProcessLifecycleOwner.get()
|
||||||
|
val processLifecycle = processLifecycleOwner.lifecycle
|
||||||
|
val processLifecycleFlow = processLifecycle
|
||||||
|
.currentStateFlow
|
||||||
|
// Convert to platform agnostic
|
||||||
|
// lifecycle state.
|
||||||
|
.map { state ->
|
||||||
|
state.toCommon()
|
||||||
|
}
|
||||||
|
processLifecycleOwner.lifecycleScope.launch {
|
||||||
|
appWorker.launch(this, processLifecycleFlow)
|
||||||
|
}
|
||||||
|
|
||||||
|
AttachmentDownloadAllWorker.enqueue(this)
|
||||||
|
|
||||||
|
// attachment clean-up
|
||||||
|
ProcessLifecycleOwner.get().bindBlock {
|
||||||
|
coroutineScope {
|
||||||
|
CleanUpAttachmentImpl.zzz(
|
||||||
|
scope = this,
|
||||||
|
downloadRepository = downloadRepository,
|
||||||
|
cleanUpAttachment = cleanUpAttachment,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// favicon
|
||||||
|
ProcessLifecycleOwner.get().bindBlock {
|
||||||
|
getVaultSession()
|
||||||
|
.map { session ->
|
||||||
|
val key = session as? MasterSession.Key
|
||||||
|
key?.di?.direct?.instance<GetAccounts>()
|
||||||
|
}
|
||||||
|
.collectLatest { getAccounts ->
|
||||||
|
if (getAccounts != null) {
|
||||||
|
coroutineScope {
|
||||||
|
Favicon.launch(this, getAccounts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// persist
|
||||||
|
ProcessLifecycleOwner.get().bindBlock {
|
||||||
|
combine(
|
||||||
|
getVaultSession(),
|
||||||
|
getVaultPersist(),
|
||||||
|
) { session, persist ->
|
||||||
|
val persistedMasterKey = if (persist && session is MasterSession.Key) {
|
||||||
|
session.masterKey
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
persistedMasterKey
|
||||||
|
}
|
||||||
|
.onEach { masterKey ->
|
||||||
|
val persistedSession = if (masterKey != null) {
|
||||||
|
PersistedSession(
|
||||||
|
masterKey = masterKey,
|
||||||
|
createdAt = Clock.System.now(),
|
||||||
|
persistedAt = Clock.System.now(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
keyReadWriteRepository.put(persistedSession).bind()
|
||||||
|
}
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// timeout
|
||||||
|
var timeoutJob: Job? = null
|
||||||
|
val getVaultLockAfterTimeout: GetVaultLockAfterTimeout by instance()
|
||||||
|
ProcessLifecycleOwner.get().bindBlock {
|
||||||
|
timeoutJob?.cancel()
|
||||||
|
timeoutJob = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
// suspend forever
|
||||||
|
suspendCancellableCoroutine<Unit> { }
|
||||||
|
} finally {
|
||||||
|
timeoutJob = getVaultLockAfterTimeout()
|
||||||
|
.toIO()
|
||||||
|
// Wait for the timeout duration.
|
||||||
|
.effectMap { duration ->
|
||||||
|
delay(duration)
|
||||||
|
duration
|
||||||
|
}
|
||||||
|
.flatMap {
|
||||||
|
// Clear the current session.
|
||||||
|
val context = LeContext(this)
|
||||||
|
val session = MasterSession.Empty(
|
||||||
|
reason = textResource(Res.strings.lock_reason_inactivity, context),
|
||||||
|
)
|
||||||
|
putVaultSession(session)
|
||||||
|
}
|
||||||
|
.attempt()
|
||||||
|
.launchIn(GlobalScope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// screen lock
|
||||||
|
val getVaultLockAfterScreenOff: GetVaultLockAfterScreenOff by instance()
|
||||||
|
val powerService: PowerService by instance()
|
||||||
|
ProcessLifecycleOwner.get().lifecycleScope.launch {
|
||||||
|
val screenFlow = powerService
|
||||||
|
.getScreenState()
|
||||||
|
.map { screen ->
|
||||||
|
val instant = Clock.System.now()
|
||||||
|
instant to screen
|
||||||
|
}
|
||||||
|
.shareIn(this, SharingStarted.Eagerly, replay = 1)
|
||||||
|
getVaultLockAfterScreenOff()
|
||||||
|
.flatMapLatest { screenLock ->
|
||||||
|
if (screenLock) {
|
||||||
|
getVaultSession()
|
||||||
|
} else {
|
||||||
|
emptyFlow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.map { session ->
|
||||||
|
when (session) {
|
||||||
|
is MasterSession.Key -> true
|
||||||
|
is MasterSession.Empty -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.map { sessionExists ->
|
||||||
|
val instant = Clock.System.now()
|
||||||
|
instant to sessionExists
|
||||||
|
}
|
||||||
|
.flatMapLatest { (sessionTimestamp, sessionExists) ->
|
||||||
|
if (sessionExists) {
|
||||||
|
return@flatMapLatest screenFlow
|
||||||
|
.mapNotNull { (screenTimestamp, screen) ->
|
||||||
|
screen
|
||||||
|
.takeIf { screenTimestamp > sessionTimestamp }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emptyFlow()
|
||||||
|
}
|
||||||
|
.filter { it is Screen.Off }
|
||||||
|
.onEach {
|
||||||
|
clearVaultSession()
|
||||||
|
.attempt()
|
||||||
|
.launchIn(this)
|
||||||
|
}
|
||||||
|
.launchIn(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
androidApp/src/main/res/drawable/ic_apps.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<!-- drawable/apps.xml -->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path android:fillColor="#000" android:pathData="M16,20H20V16H16M16,14H20V10H16M10,8H14V4H10M16,8H20V4H16M10,14H14V10H10M4,14H8V10H4M4,20H8V16H4M10,20H14V16H10M4,8H8V4H4V8Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_cancel.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M12 2C17.5 2 22 6.5 22 12S17.5 22 12 22 2 17.5 2 12 6.5 2 12 2M12 4C10.1 4 8.4 4.6 7.1 5.7L18.3 16.9C19.3 15.5 20 13.8 20 12C20 7.6 16.4 4 12 4M16.9 18.3L5.7 7.1C4.6 8.4 4 10.1 4 12C4 16.4 7.6 20 12 20C13.9 20 15.6 19.4 16.9 18.3Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_copy.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_download.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z" />
|
||||||
|
</vector>
|
@ -0,0 +1,3 @@
|
|||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="@color/launcher_background" />
|
||||||
|
</shape>
|
38
androidApp/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M54.5,34L54.5,34A13.5,13.5 0,0 1,68 47.5L68,52.5A13.5,13.5 0,0 1,54.5 66L54.5,66A13.5,13.5 0,0 1,41 52.5L41,47.5A13.5,13.5 0,0 1,54.5 34z"
|
||||||
|
android:strokeWidth="6"
|
||||||
|
android:strokeColor="@color/launcher_handle" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/launcher_body_1"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M69,50C71.14,50 72.89,51.68 73,53.8L73,54L73,63.33C73,71.43 66.43,78 58.33,78L50.67,78C42.57,78 36,71.43 36,63.33L36,54C36,51.79 37.79,50 40,50L69,50Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/launcher_body_2"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M67,50C69.14,50 70.89,51.68 71,53.8L71,54L71,63.33C71,71.43 64.43,78 56.33,78L50.67,78C42.57,78 36,71.43 36,63.33L36,54C36,51.79 37.79,50 40,50L67,50Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/launcher_body_3"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M67,50C69.14,50 70.89,51.68 71,53.8L71,54L71,63.33C71,71.43 64.43,78 56.33,78L52.67,78C44.57,78 38,71.43 38,63.33L38,54C38,51.79 39.79,50 42,50L67,50Z" />
|
||||||
|
<path
|
||||||
|
android:fillAlpha="0.15675427"
|
||||||
|
android:fillColor="#282828"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M54,56C57.31,56 60,58.35 60,61.25C60,63.34 58.6,65.15 56.57,65.99L56.57,69.5C56.57,70.33 55.9,71 55.07,71L52.93,71C52.1,71 51.43,70.33 51.43,69.5L51.43,65.99C49.4,65.15 48,63.34 48,61.25C48,58.35 50.69,56 54,56Z" />
|
||||||
|
<path
|
||||||
|
android:fillAlpha="0.13071585"
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M56,56C59.31,56 62,58.35 62,61.25C62,63.34 60.6,65.15 58.57,65.99L58.57,69.5C58.57,70.33 57.9,71 57.07,71L54.93,71C54.1,71 53.43,70.33 53.43,69.5L53.43,65.99C51.4,65.15 50,63.34 50,61.25C50,58.35 52.69,56 56,56Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/launcher_handle"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M54.5,56C57.54,56 60,58.35 60,61.25C60,63.34 58.72,65.15 56.86,65.99L56.86,69.5C56.86,70.33 56.19,71 55.36,71L53.64,71C52.81,71 52.14,70.33 52.14,69.5L52.14,65.99C50.28,65.15 49,63.34 49,61.25C49,58.35 51.46,56 54.5,56Z" />
|
||||||
|
</vector>
|
18
androidApp/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:pathData="M56,31C64.28,31 71,37.72 71,46L71,53L65,53L65,46C65,41.12 61.11,37.14 56.27,37L56,37L53,37C48.12,37 44.14,40.89 44,45.73L44,46L44,53L38,53L38,46C38,37.72 44.72,31 53,31L56,31Z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:fillColor="#ffffffff"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:strokeColor="#00000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M67,50C69.14,50 70.89,51.68 71,53.8L71,54L71,63.33C71,71.43 64.43,78 56.33,78L52.67,78C44.57,78 38,71.43 38,63.33L38,54C38,51.79 39.79,50 42,50L67,50ZM54.5,56C51.46,56 49,58.35 49,61.25C49,63.34 50.28,65.15 52.14,65.99L52.14,65.99L52.14,69.5C52.14,70.33 52.81,71 53.64,71L53.64,71L55.36,71C56.19,71 56.86,70.33 56.86,69.5L56.86,69.5L56.86,65.99C58.72,65.15 60,63.34 60,61.25C60,58.35 57.54,56 54.5,56Z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:fillColor="#E0F3FF"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#00000000"/>
|
||||||
|
</vector>
|
@ -0,0 +1,8 @@
|
|||||||
|
<!-- drawable/lock_open_outline.xml -->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path android:fillColor="#000" android:pathData="M18,20V10H6V20H18M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10A2,2 0 0,1 6,8H15V6A3,3 0 0,0 12,3A3,3 0 0,0 9,6H7A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,17A2,2 0 0,1 10,15A2,2 0 0,1 12,13A2,2 0 0,1 14,15A2,2 0 0,1 12,17Z" />
|
||||||
|
</vector>
|
8
androidApp/src/main/res/drawable/ic_lock_outline.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<!-- drawable/lock_outline.xml -->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path android:fillColor="#000" android:pathData="M12,17C10.89,17 10,16.1 10,15C10,13.89 10.89,13 12,13A2,2 0 0,1 14,15A2,2 0 0,1 12,17M18,20V10H6V20H18M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10C4,8.89 4.89,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_login.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="640dp"
|
||||||
|
android:height="512dp"
|
||||||
|
android:viewportWidth="640"
|
||||||
|
android:viewportHeight="512">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF777777"
|
||||||
|
android:pathData="M571.2 205.825c-9.082-45.471-30.339-87.624-61.501-121.963-31.159-34.339-71.056-59.575-115.437-73.018-44.377-13.443-91.573-14.586-136.551-3.311s-86.052 34.55-118.84 67.338c-32.788 32.788-56.062 73.862-67.338 118.84s-10.132 92.173 3.311 136.551c13.442 44.381 38.679 84.279 73.018 115.437 34.339 31.161 76.492 52.419 121.964 61.501 41.367 8.409 84.172 6.442 124.594-5.728 40.419-12.166 77.197-34.157 107.046-64.007s51.84-66.627 64.007-107.046c12.169-40.422 14.138-83.228 5.728-124.594zM186.305 76.481c38.576-28.909 85.49-44.517 133.695-44.48 2.752 0 5.376 0.48 8.128 0.608 9.952 2.88 2.368 11.104 2.368 11.104-34.061 17.672-62.726 44.203-82.975 76.8-9.6 15.488-17.216 15.328-19.68 15.040-14.112-0.608-31.456-20.224-44.608-40.16-1.954-2.963-2.723-6.552-2.154-10.055s2.434-6.665 5.226-8.856v0zM253.505 461.184c-1.923 1.971-4.388 3.328-7.083 3.897s-5.497 0.327-8.054-0.697c-40.582-15.911-75.645-43.305-100.902-78.832s-39.611-77.643-41.306-121.199l9.44-13.888c30.432-11.36 60.416-17.504 68.512-10.496 3.744 3.2 1.184 13.12 0 17.216-22.88 74.143 36.288 93.183 58.624 100.383 2.464 0.8 4.576 1.504 6.4 2.112 23.392 9.024 36.736 22.112 39.712 38.848 0.56 11.645-1.404 23.274-5.757 34.087s-10.99 20.56-19.459 28.57h-0.128zM477.152 414.656c-41.667 41.728-98.183 65.226-157.152 65.344-9.798-0.067-19.581-0.807-29.279-2.208-1.884-0.298-3.663-1.063-5.178-2.221-1.514-1.161-2.716-2.678-3.498-4.419s-1.117-3.648-0.976-5.549c0.14-1.901 0.752-3.741 1.779-5.347v0c14.127-18.771 20.486-42.262 17.76-65.6-3.027-13.315-9.63-25.552-19.099-35.392s-21.44-16.909-34.63-20.448c-1.984-0.768-4.544-1.632-7.488-2.56-30.592-9.856-59.168-23.551-44.192-72.447 7.040-22.848 0.256-34.784-6.656-40.768-14.72-12.8-42.976-8.928-68.96-1.216-2.377 0.642-4.879 0.653-7.262 0.034s-4.563-1.849-6.326-3.566c-1.763-1.718-3.049-3.865-3.73-6.231s-0.734-4.868-0.155-7.26c7.821-33.432 23.251-64.608 45.088-91.104 1.379-1.599 3.102-2.864 5.042-3.698s4.043-1.216 6.151-1.118c2.109 0.099 4.167 0.675 6.020 1.688s3.452 2.431 4.674 4.152c14.304 20.224 35.040 42.4 57.6 43.392h1.6c8.108-0.433 15.946-3.062 22.676-7.605s12.098-10.829 15.532-18.188c25.389-38.702 62.678-68.096 106.239-83.744v0c9.504-2.946 19.648-3.124 29.248-0.512 29.693 11.669 56.55 29.538 78.784 52.416 1.798 1.852 3.053 4.159 3.635 6.674s0.467 5.138-0.333 7.591c-0.8 2.453-2.256 4.641-4.208 6.329s-4.327 2.81-6.87 3.247c-41.088 7.136-94.208 21.184-101.088 46.816-3.2 12.416 2.912 24.48 18.56 35.872 60.288 43.744 77.984 77.344 68.768 91.232-8.49 10.898-14.24 23.673-16.774 37.254-2.534 13.577-1.773 27.568 2.214 40.793 5.907 10.624 15.488 18.726 26.944 22.784 0 0 11.36 6.528 5.664 15.584h-0.128zM512 369.664c-2.966 3.639-6.743 6.531-11.024 8.448-4.285 1.917-8.96 2.807-13.648 2.592-4.595-0.729-8.992-2.409-12.909-4.925-3.917-2.519-7.267-5.821-9.843-9.699-7.584-14.624 2.432-38.944 13.312-55.359 11.296-17.12 16.736-55.68-74.304-121.76-7.648-5.536-10.592-10.080-10.080-11.936 3.2-11.808 52.608-27.072 108.8-34.336 2.845-0.366 5.735 0.123 8.298 1.405 2.567 1.282 4.691 3.299 6.102 5.794 18.557 33.828 27.891 71.942 27.066 110.518s-11.783 76.255-31.769 109.257v0z" />
|
||||||
|
</vector>
|
BIN
androidApp/src/main/res/drawable/ic_logo_bitwarden.png
Normal file
After Width: | Height: | Size: 13 KiB |
9
androidApp/src/main/res/drawable/ic_number_0.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M11,7H13A2,2 0 0,1 15,9V15A2,2 0 0,1 13,17H11A2,2 0 0,1 9,15V9A2,2 0 0,1 11,7M11,9V15H13V9H11M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_number_1.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M10,7H14V17H12V9H10V7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_number_2.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M9,7H13A2,2 0 0,1 15,9V11A2,2 0 0,1 13,13H11V15H15V17H11L9,17V13A2,2 0 0,1 11,11H13V9H9V7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_number_3.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M15,15A2,2 0 0,1 13,17H9V15H13V13H11V11H13V9H9V7H13A2,2 0 0,1 15,9V10.5A1.5,1.5 0 0,1 13.5,12A1.5,1.5 0 0,1 15,13.5V15M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_number_4.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M9,7H11V11H13V7H15V17H13V13H9V7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_number_5.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M9,7H15V9H11V11H13A2,2 0 0,1 15,13V15A2,2 0 0,1 13,17H9V15H13V13H9V7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_number_6.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M11,7H15V9H11V11H13A2,2 0 0,1 15,13V15A2,2 0 0,1 13,17H11A2,2 0 0,1 9,15V9A2,2 0 0,1 11,7M11,13V15H13V13H11M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_number_7.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M11,17H9L13,9H9V7H15V9L11,17M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_number_8.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M11,13V15H13V13H11M11,9V11H13V9H11M11,17A2,2 0 0,1 9,15V13.5A1.5,1.5 0 0,1 10.5,12A1.5,1.5 0 0,1 9,10.5V9A2,2 0 0,1 11,7H13A2,2 0 0,1 15,9V10.5A1.5,1.5 0 0,1 13.5,12A1.5,1.5 0 0,1 15,13.5V15A2,2 0 0,1 13,17H11M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_number_9.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M13,17H9V15H13V13H11A2,2 0 0,1 9,11V9A2,2 0 0,1 11,7H13A2,2 0 0,1 15,9V15A2,2 0 0,1 13,17M13,11V9H11V11H13M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_number_9_plus.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M19,11V13H17V15H15V13H13V11H15V9H17V11H19M10,7A2,2 0 0,1 12,9V15C12,16.11 11.1,17 10,17H6V15H10V13H8A2,2 0 0,1 6,11V9C6,7.89 6.9,7 8,7H10M8,9V11H10V9H8M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />
|
||||||
|
</vector>
|
4
androidApp/src/main/res/drawable/ic_splash.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<item android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</layer-list>
|
9
androidApp/src/main/res/drawable/ic_tfa.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M5.41,21L6.12,17H2.12L2.47,15H6.47L7.53,9H3.53L3.88,7H7.88L8.59,3H10.59L9.88,7H15.88L16.59,3H18.59L17.88,7H21.88L21.53,9H17.53L16.47,15H20.47L20.12,17H16.12L15.41,21H13.41L14.12,17H8.12L7.41,21H5.41M9.53,9L8.47,15H14.47L15.53,9H9.53Z" />
|
||||||
|
</vector>
|
9
androidApp/src/main/res/drawable/ic_upload.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000"
|
||||||
|
android:pathData="M9,16V10H5L12,3L19,10H15V16H9M5,20V18H19V20H5Z" />
|
||||||
|
</vector>
|
8
androidApp/src/main/res/drawable/ic_web.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<!-- drawable/web.xml -->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path android:fillColor="#000" android:pathData="M16.36,14C16.44,13.34 16.5,12.68 16.5,12C16.5,11.32 16.44,10.66 16.36,10H19.74C19.9,10.64 20,11.31 20,12C20,12.69 19.9,13.36 19.74,14M14.59,19.56C15.19,18.45 15.65,17.25 15.97,16H18.92C17.96,17.65 16.43,18.93 14.59,19.56M14.34,14H9.66C9.56,13.34 9.5,12.68 9.5,12C9.5,11.32 9.56,10.65 9.66,10H14.34C14.43,10.65 14.5,11.32 14.5,12C14.5,12.68 14.43,13.34 14.34,14M12,19.96C11.17,18.76 10.5,17.43 10.09,16H13.91C13.5,17.43 12.83,18.76 12,19.96M8,8H5.08C6.03,6.34 7.57,5.06 9.4,4.44C8.8,5.55 8.35,6.75 8,8M5.08,16H8C8.35,17.25 8.8,18.45 9.4,19.56C7.57,18.93 6.03,17.65 5.08,16M4.26,14C4.1,13.36 4,12.69 4,12C4,11.31 4.1,10.64 4.26,10H7.64C7.56,10.66 7.5,11.32 7.5,12C7.5,12.68 7.56,13.34 7.64,14M12,4.03C12.83,5.23 13.5,6.57 13.91,8H10.09C10.5,6.57 11.17,5.23 12,4.03M18.92,8H15.97C15.65,6.75 15.19,5.55 14.59,4.44C16.43,5.07 17.96,6.34 18.92,8M12,2C6.47,2 2,6.5 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
|
||||||
|
</vector>
|
11
androidApp/src/main/res/layout/activity_webview.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<WebView
|
||||||
|
android:id="@+id/webView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
</LinearLayout>
|
46
androidApp/src/main/res/layout/item_autofill.xml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/lightgray"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingLeft="10dp"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:paddingRight="10dp"
|
||||||
|
android:paddingBottom="5dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/icon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:maxWidth="20dp"
|
||||||
|
android:maxHeight="20dp"
|
||||||
|
android:src="@drawable/ic_login" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text1"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textColor="@color/black"
|
||||||
|
tools:text="Name" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textColor="@color/gray"
|
||||||
|
tools:text="Username" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
35
androidApp/src/main/res/layout/item_autofill_app_id.xml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/launcher_background"
|
||||||
|
android:minHeight="24dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/autofill_app_id_icon"
|
||||||
|
android:layout_width="18dp"
|
||||||
|
android:layout_height="18dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:contentDescription="@string/about"
|
||||||
|
android:src="@drawable/ic_apps"
|
||||||
|
android:tint="@color/launcher_body_2"
|
||||||
|
tools:ignore="UseAppTint" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/autofill_app_id_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingLeft="8dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:textColor="@color/launcher_body_2"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</LinearLayout>
|
53
androidApp/src/main/res/layout/item_autofill_entry.xml
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?android:attr/windowBackground"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/autofill_entry_icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:src="@drawable/ic_web"
|
||||||
|
android:tint="?android:colorControlNormal"
|
||||||
|
tools:ignore="UseAppTint" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingLeft="0dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingRight="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/autofill_entry_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItemSmall" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/autofill_entry_username"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:alpha="0.6"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItemSmall"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/windowBackground"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:contentDescription="@string/autofill_open_keyguard"
|
||||||
|
android:src="@mipmap/ic_launcher" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:text="@string/autofill_open_keyguard"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItemSmall" />
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include layout="@layout/item_autofill_app_id" />
|
||||||
|
|
||||||
|
<include layout="@layout/item_autofill_select_entry" />
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include layout="@layout/item_autofill_web_domain" />
|
||||||
|
|
||||||
|
<include layout="@layout/item_autofill_select_entry" />
|
||||||
|
</LinearLayout>
|
28
androidApp/src/main/res/layout/item_autofill_unlock.xml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/windowBackground"
|
||||||
|
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:src="@mipmap/ic_launcher" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:text="@string/autofill_unlock_keyguard"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItemSmall" />
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include layout="@layout/item_autofill_app_id" />
|
||||||
|
|
||||||
|
<include layout="@layout/item_autofill_unlock" />
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include layout="@layout/item_autofill_web_domain" />
|
||||||
|
|
||||||
|
<include layout="@layout/item_autofill_unlock" />
|
||||||
|
</LinearLayout>
|
48
androidApp/src/main/res/layout/item_autofill_web_domain.xml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
* Copyright (C) 2017 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/launcher_background"
|
||||||
|
android:minHeight="24dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/autofill_web_domain_icon"
|
||||||
|
android:layout_width="18dp"
|
||||||
|
android:layout_height="18dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:src="@drawable/ic_web"
|
||||||
|
android:tint="@color/launcher_body_2"
|
||||||
|
tools:ignore="UseAppTint" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/autofill_web_domain_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingLeft="8dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:textColor="@color/launcher_body_2"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
|
||||||
|
</adaptive-icon>
|
BIN
androidApp/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
androidApp/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
androidApp/src/main/res/raw/silence
Normal file
1
androidApp/src/main/res/resources.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
unqualifiedResLocale=en-US
|
8
androidApp/src/main/res/values-v31/colors.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="launcher_background">@android:color/system_accent1_400</color>
|
||||||
|
<color name="launcher_handle">@android:color/system_neutral1_900</color>
|
||||||
|
<color name="launcher_body_1">@android:color/system_accent1_100</color>
|
||||||
|
<color name="launcher_body_2">@android:color/system_accent2_50</color>
|
||||||
|
<color name="launcher_body_3">@android:color/system_accent2_50</color>
|
||||||
|
</resources>
|
22
androidApp/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="launcher_background">#FF219BCC</color>
|
||||||
|
<color name="launcher_handle">#FF191C1E</color>
|
||||||
|
<color name="launcher_body_1">#FFC1E8FF</color>
|
||||||
|
<color name="launcher_body_2">#FFE0F3FF</color>
|
||||||
|
<color name="launcher_body_3">#FFE0F3FF</color>
|
||||||
|
|
||||||
|
<color name="purple_200">#FFBB86FC</color>
|
||||||
|
<color name="purple_500">#FF6200EE</color>
|
||||||
|
<color name="purple_700">#FF3700B3</color>
|
||||||
|
<color name="teal_200">#FF03DAC5</color>
|
||||||
|
<color name="teal_700">#FF018786</color>
|
||||||
|
<color name="black">#FF000000</color>
|
||||||
|
<color name="white">#FFFFFFFF</color>
|
||||||
|
<color name="green">#43a047</color>
|
||||||
|
|
||||||
|
<!-- Other -->
|
||||||
|
<color name="darkgray">#333333</color>
|
||||||
|
<color name="gray">#738182</color>
|
||||||
|
<color name="lightgray">#efeff4</color>
|
||||||
|
</resources>
|
4
androidApp/src/main/res/values/ids.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<resources>
|
||||||
|
<item name="notification_attachment_upload_id" type="integer">1100</item>
|
||||||
|
<item name="notification_attachment_download_id" type="integer">1200</item>
|
||||||
|
</resources>
|
23
androidApp/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">Keyguard</string>
|
||||||
|
|
||||||
|
<string name="notification_attachment_upload_channel_id">com.artemchep.keyguard.UPLOAD_ATTACHMENTS</string>
|
||||||
|
<string name="notification_attachment_upload_channel_name">Uploading attachments</string>
|
||||||
|
<string name="notification_attachment_upload_title">Uploading attachments</string>
|
||||||
|
<string name="notification_attachment_upload_content">Uploading attachments</string>
|
||||||
|
|
||||||
|
<string name="notification_attachment_download_channel_id">com.artemchep.keyguard.DOWNLOAD_ATTACHMENTS</string>
|
||||||
|
<string name="notification_attachment_download_channel_name">Download attachments</string>
|
||||||
|
|
||||||
|
<string name="notification_clipboard_channel_id">com.artemchep.keyguard.CLIPBOARD</string>
|
||||||
|
<string name="notification_clipboard_channel_name">Clipboard</string>
|
||||||
|
|
||||||
|
<string name="about">About</string>
|
||||||
|
<string name="autofill">Autofill</string>
|
||||||
|
<string name="autofill_service_name">Keyguard form autofilling</string>
|
||||||
|
<string name="autofill_sign_in_prompt">Unlock Keyguard</string>
|
||||||
|
<string name="autofill_explanation_summary">Enable autofilling to quickly fill out forms in other apps</string>
|
||||||
|
<string name="autofill_select_entry">Open Keyguard</string>
|
||||||
|
<string name="set_autofill_service_title">Set default autofill service</string>
|
||||||
|
<string name="autofill_preference_title">Autofill settings</string>
|
||||||
|
</resources>
|
14
androidApp/src/main/res/values/themes.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<style name="Theme.Keyguard.Splash" parent="Theme.SplashScreen">
|
||||||
|
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash</item>
|
||||||
|
<item name="windowSplashScreenAnimationDuration">0</item>
|
||||||
|
|
||||||
|
<item name="postSplashScreenTheme">@style/Theme.Keyguard</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="Theme.Keyguard" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||||
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||||
|
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
42915
androidApp/src/noneRelease/generated/baselineProfiles/baseline-prof.txt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package com.artemchep.keyguard
|
||||||
|
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
fun addition_isCorrect() = runBlocking {
|
||||||
|
}
|
||||||
|
}
|
80
androidBenchmark/build.gradle.kts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import com.android.build.api.dsl.ManagedVirtualDevice
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.test)
|
||||||
|
alias(libs.plugins.kotlin.android)
|
||||||
|
alias(libs.plugins.baseline.profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdk = libs.versions.androidCompileSdk.get().toInt()
|
||||||
|
namespace = "com.artemchep.macrobenchmark"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = libs.versions.androidMinSdk.get().toInt()
|
||||||
|
targetSdk = libs.versions.androidTargetSdk.get().toInt()
|
||||||
|
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "11"
|
||||||
|
}
|
||||||
|
testOptions.managedDevices.devices {
|
||||||
|
maybeCreate<ManagedVirtualDevice>("pixel6api34").apply {
|
||||||
|
device = "Pixel 6"
|
||||||
|
apiLevel = 34
|
||||||
|
systemImageSource = "google_apis_playstore"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
targetProjectPath = ":androidApp"
|
||||||
|
// Enable the benchmark to run separately from the app process
|
||||||
|
experimentalProperties["android.experimental.self-instrumenting"] = true
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
// declare a build type to match the target app"s build type
|
||||||
|
create("benchmark") {
|
||||||
|
isDebuggable = true
|
||||||
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
|
// Selects release buildType if the 'benchmark'
|
||||||
|
// buildType not available in other modules.
|
||||||
|
matchingFallbacks.add("release")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
buildConfig = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val accountManagementDimension = "accountManagement"
|
||||||
|
flavorDimensions += accountManagementDimension
|
||||||
|
productFlavors {
|
||||||
|
maybeCreate("playStore").apply {
|
||||||
|
dimension = accountManagementDimension
|
||||||
|
}
|
||||||
|
maybeCreate("none").apply {
|
||||||
|
dimension = accountManagementDimension
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
baselineProfile {
|
||||||
|
managedDevices += "pixel6api34"
|
||||||
|
|
||||||
|
// This enables using connected devices to generate profiles. The default is true.
|
||||||
|
// When using connected devices, they must be rooted or API 33 and higher.
|
||||||
|
useConnectedDevices = false
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.androidx.benchmark.macro.junit4)
|
||||||
|
implementation(libs.androidx.espresso.core)
|
||||||
|
implementation(libs.androidx.junit)
|
||||||
|
implementation(libs.androidx.uiautomator)
|
||||||
|
implementation(libs.androidx.profileinstaller)
|
||||||
|
}
|
11
androidBenchmark/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!-- Requesting legacy storage is needed to be able to write to additionalTestOutputDir on API 29 -->
|
||||||
|
<application android:requestLegacyExternalStorage="true" />
|
||||||
|
|
||||||
|
<queries>
|
||||||
|
<package android:name="com.artemchep.keyguard" />
|
||||||
|
</queries>
|
||||||
|
|
||||||
|
</manifest>
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.artemchep.macrobenchmark
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience parameter to use proper package name
|
||||||
|
* with regards to build type and build flavor.
|
||||||
|
*/
|
||||||
|
val PACKAGE_NAME = StringBuilder("com.artemchep.keyguard").apply {
|
||||||
|
val hasSuffix = when (BuildConfig.BUILD_TYPE) {
|
||||||
|
"debug" -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
if (hasSuffix) {
|
||||||
|
append(".${BuildConfig.BUILD_TYPE}")
|
||||||
|
}
|
||||||
|
}.toString()
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.artemchep.macrobenchmark.baselineprofile
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.benchmark.macro.junit4.BaselineProfileRule
|
||||||
|
import com.artemchep.macrobenchmark.PACKAGE_NAME
|
||||||
|
import com.artemchep.macrobenchmark.ui.keyguard.createVaultAndWait
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a baseline profile which
|
||||||
|
* can be copied to `app/src/main/baseline-prof.txt`.
|
||||||
|
*/
|
||||||
|
@RequiresApi(Build.VERSION_CODES.P)
|
||||||
|
class BaselineProfileGenerator {
|
||||||
|
@get:Rule
|
||||||
|
val baselineProfileRule = BaselineProfileRule()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generate() = baselineProfileRule.collect(
|
||||||
|
packageName = PACKAGE_NAME,
|
||||||
|
) {
|
||||||
|
// This block defines the app's critical user journey. Here we are interested in
|
||||||
|
// optimizing for app startup. But you can also navigate and scroll
|
||||||
|
// through your most important UI.
|
||||||
|
|
||||||
|
pressHome()
|
||||||
|
startActivityAndWait()
|
||||||
|
|
||||||
|
createVaultAndWait()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.artemchep.macrobenchmark.ui.keyguard
|
||||||
|
|
||||||
|
import androidx.benchmark.macro.MacrobenchmarkScope
|
||||||
|
import androidx.test.uiautomator.By
|
||||||
|
import androidx.test.uiautomator.Until
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
fun MacrobenchmarkScope.waitForMainScreen() = kotlin.run {
|
||||||
|
val search = kotlin.run {
|
||||||
|
val pattern = Pattern.compile("nav:(setup|unlock|main)")
|
||||||
|
val selector = By.res(pattern)
|
||||||
|
Until.findObject(selector)
|
||||||
|
}
|
||||||
|
device.wait(search, 30_000)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun MacrobenchmarkScope.createVaultAndWait() = kotlin.run {
|
||||||
|
val screen = waitForMainScreen()
|
||||||
|
when (screen.resourceName) {
|
||||||
|
"nav:setup",
|
||||||
|
"nav:unlock",
|
||||||
|
-> {
|
||||||
|
// Create or unlock existing vault
|
||||||
|
val password = screen
|
||||||
|
.findObject(By.res("field:password"))
|
||||||
|
password.text = "111111"
|
||||||
|
val btn = screen
|
||||||
|
.findObject(By.res("btn:go"))
|
||||||
|
btn.wait(Until.clickable(true), 1000L)
|
||||||
|
btn.click()
|
||||||
|
// wait till main screen is loaded
|
||||||
|
device.wait(Until.findObject(By.res("nav:main")), 30_000)
|
||||||
|
}
|
||||||
|
else -> screen
|
||||||
|
}
|
||||||
|
}
|
BIN
artwork/android-adaptive-icon.sketch
Normal file
BIN
artwork/ic_launcher_web.png
Normal file
After Width: | Height: | Size: 21 KiB |