Monorepo initial commit
6
companion/.expo-shared/assets.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"e997a5256149a4b76e6bfd6cbf519c5e5a0f1d278a3d8fa1253022b03c90473b": true,
|
||||||
|
"af683c96e0ffd2cf81287651c9433fa44debc1220ca7cb431fe482747f34a505": true,
|
||||||
|
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
|
||||||
|
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
|
||||||
|
}
|
73
companion/.gitignore
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
node_modules/
|
||||||
|
.expo/
|
||||||
|
npm-debug.*
|
||||||
|
*.jks
|
||||||
|
*.p8
|
||||||
|
*.p12
|
||||||
|
*.key
|
||||||
|
*.mobileprovision
|
||||||
|
*.orig.*
|
||||||
|
web-build/
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# @generated expo-cli sync-2138f1e3e130677ea10ea873f6d498e3890e677b
|
||||||
|
# The following patterns were generated by expo-cli
|
||||||
|
|
||||||
|
# OSX
|
||||||
|
#
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Xcode
|
||||||
|
#
|
||||||
|
build/
|
||||||
|
*.pbxuser
|
||||||
|
!default.pbxuser
|
||||||
|
*.mode1v3
|
||||||
|
!default.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
!default.mode2v3
|
||||||
|
*.perspectivev3
|
||||||
|
!default.perspectivev3
|
||||||
|
xcuserdata
|
||||||
|
*.xccheckout
|
||||||
|
*.moved-aside
|
||||||
|
DerivedData
|
||||||
|
*.hmap
|
||||||
|
*.ipa
|
||||||
|
*.xcuserstate
|
||||||
|
project.xcworkspace
|
||||||
|
|
||||||
|
# Android/IntelliJ
|
||||||
|
#
|
||||||
|
build/
|
||||||
|
.idea
|
||||||
|
.gradle
|
||||||
|
local.properties
|
||||||
|
*.iml
|
||||||
|
*.hprof
|
||||||
|
|
||||||
|
# node.js
|
||||||
|
#
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
# BUCK
|
||||||
|
buck-out/
|
||||||
|
\.buckd/
|
||||||
|
*.keystore
|
||||||
|
!debug.keystore
|
||||||
|
|
||||||
|
# Bundle artifacts
|
||||||
|
*.jsbundle
|
||||||
|
|
||||||
|
# CocoaPods
|
||||||
|
/ios/Pods/
|
||||||
|
|
||||||
|
# Expo
|
||||||
|
.expo/
|
||||||
|
web-build/
|
||||||
|
|
||||||
|
# @end expo-cli
|
55
companion/android/app/BUCK
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# To learn about Buck see [Docs](https://buckbuild.com/).
|
||||||
|
# To run your application with Buck:
|
||||||
|
# - install Buck
|
||||||
|
# - `npm start` - to start the packager
|
||||||
|
# - `cd android`
|
||||||
|
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
|
||||||
|
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
|
||||||
|
# - `buck install -r android/app` - compile, install and run application
|
||||||
|
#
|
||||||
|
|
||||||
|
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
|
||||||
|
|
||||||
|
lib_deps = []
|
||||||
|
|
||||||
|
create_aar_targets(glob(["libs/*.aar"]))
|
||||||
|
|
||||||
|
create_jar_targets(glob(["libs/*.jar"]))
|
||||||
|
|
||||||
|
android_library(
|
||||||
|
name = "all-libs",
|
||||||
|
exported_deps = lib_deps,
|
||||||
|
)
|
||||||
|
|
||||||
|
android_library(
|
||||||
|
name = "app-code",
|
||||||
|
srcs = glob([
|
||||||
|
"src/main/java/**/*.java",
|
||||||
|
]),
|
||||||
|
deps = [
|
||||||
|
":all-libs",
|
||||||
|
":build_config",
|
||||||
|
":res",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
android_build_config(
|
||||||
|
name = "build_config",
|
||||||
|
package = "com.aurailus.focuscompanion",
|
||||||
|
)
|
||||||
|
|
||||||
|
android_resource(
|
||||||
|
name = "res",
|
||||||
|
package = "com.aurailus.focuscompanion",
|
||||||
|
res = "src/main/res",
|
||||||
|
)
|
||||||
|
|
||||||
|
android_binary(
|
||||||
|
name = "app",
|
||||||
|
keystore = "//android/keystores:debug",
|
||||||
|
manifest = "src/main/AndroidManifest.xml",
|
||||||
|
package_type = "debug",
|
||||||
|
deps = [
|
||||||
|
":app-code",
|
||||||
|
],
|
||||||
|
)
|
227
companion/android/app/build.gradle
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
apply plugin: "com.android.application"
|
||||||
|
apply plugin: "kotlin-android"
|
||||||
|
apply plugin: "kotlin-android-extensions"
|
||||||
|
|
||||||
|
import com.android.build.OutputFile
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
||||||
|
* and bundleReleaseJsAndAssets).
|
||||||
|
* These basically call `react-native bundle` with the correct arguments during the Android build
|
||||||
|
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
|
||||||
|
* bundle directly from the development server. Below you can see all the possible configurations
|
||||||
|
* and their defaults. If you decide to add a configuration block, make sure to add it before the
|
||||||
|
* `apply from: "../../node_modules/react-native/react.gradle"` line.
|
||||||
|
*
|
||||||
|
* project.ext.react = [
|
||||||
|
* // the name of the generated asset file containing your JS bundle
|
||||||
|
* bundleAssetName: "index.android.bundle",
|
||||||
|
*
|
||||||
|
* // the entry file for bundle generation. If none specified and
|
||||||
|
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
|
||||||
|
* // default. Can be overridden with ENTRY_FILE environment variable.
|
||||||
|
* entryFile: "index.android.js",
|
||||||
|
*
|
||||||
|
* // https://reactnative.dev/docs/performance#enable-the-ram-format
|
||||||
|
* bundleCommand: "ram-bundle",
|
||||||
|
*
|
||||||
|
* // whether to bundle JS and assets in debug mode
|
||||||
|
* bundleInDebug: false,
|
||||||
|
*
|
||||||
|
* // whether to bundle JS and assets in release mode
|
||||||
|
* bundleInRelease: true,
|
||||||
|
*
|
||||||
|
* // whether to bundle JS and assets in another build variant (if configured).
|
||||||
|
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
|
||||||
|
* // The configuration property can be in the following formats
|
||||||
|
* // 'bundleIn${productFlavor}${buildType}'
|
||||||
|
* // 'bundleIn${buildType}'
|
||||||
|
* // bundleInFreeDebug: true,
|
||||||
|
* // bundleInPaidRelease: true,
|
||||||
|
* // bundleInBeta: true,
|
||||||
|
*
|
||||||
|
* // whether to disable dev mode in custom build variants (by default only disabled in release)
|
||||||
|
* // for example: to disable dev mode in the staging build type (if configured)
|
||||||
|
* devDisabledInStaging: true,
|
||||||
|
* // The configuration property can be in the following formats
|
||||||
|
* // 'devDisabledIn${productFlavor}${buildType}'
|
||||||
|
* // 'devDisabledIn${buildType}'
|
||||||
|
*
|
||||||
|
* // the root of your project, i.e. where "package.json" lives
|
||||||
|
* root: "../../",
|
||||||
|
*
|
||||||
|
* // where to put the JS bundle asset in debug mode
|
||||||
|
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
|
||||||
|
*
|
||||||
|
* // where to put the JS bundle asset in release mode
|
||||||
|
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
|
||||||
|
*
|
||||||
|
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||||
|
* // require('./image.png')), in debug mode
|
||||||
|
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
|
||||||
|
*
|
||||||
|
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||||
|
* // require('./image.png')), in release mode
|
||||||
|
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
|
||||||
|
*
|
||||||
|
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
|
||||||
|
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
|
||||||
|
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
|
||||||
|
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
|
||||||
|
* // for example, you might want to remove it from here.
|
||||||
|
* inputExcludes: ["android/**", "ios/**"],
|
||||||
|
*
|
||||||
|
* // override which node gets called and with what additional arguments
|
||||||
|
* nodeExecutableAndArgs: ["node"],
|
||||||
|
*
|
||||||
|
* // supply additional arguments to the packager
|
||||||
|
* extraPackagerArgs: []
|
||||||
|
* ]
|
||||||
|
*/
|
||||||
|
|
||||||
|
project.ext.react = [
|
||||||
|
enableHermes: (findProperty('expo.jsEngine') ?: "jsc") == "hermes",
|
||||||
|
]
|
||||||
|
|
||||||
|
apply from: '../../node_modules/react-native-unimodules/gradle.groovy'
|
||||||
|
apply from: "../../node_modules/react-native/react.gradle"
|
||||||
|
apply from: "../../node_modules/expo-constants/scripts/get-app-config-android.gradle"
|
||||||
|
apply from: "../../node_modules/expo-updates/scripts/create-manifest-android.gradle"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this to true to create two separate APKs instead of one:
|
||||||
|
* - An APK that only works on ARM devices
|
||||||
|
* - An APK that only works on x86 devices
|
||||||
|
* The advantage is the size of the APK is reduced by about 4MB.
|
||||||
|
* Upload all the APKs to the Play Store and people will download
|
||||||
|
* the correct one based on the CPU architecture of their device.
|
||||||
|
*/
|
||||||
|
def enableSeparateBuildPerCPUArchitecture = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run Proguard to shrink the Java bytecode in release builds.
|
||||||
|
*/
|
||||||
|
def enableProguardInReleaseBuilds = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preferred build flavor of JavaScriptCore.
|
||||||
|
*
|
||||||
|
* For example, to use the international variant, you can use:
|
||||||
|
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
|
||||||
|
*
|
||||||
|
* The international variant includes ICU i18n library and necessary data
|
||||||
|
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
||||||
|
* give correct results when using with locales other than en-US. Note that
|
||||||
|
* this variant is about 6MiB larger per architecture than default.
|
||||||
|
*/
|
||||||
|
def jscFlavor = 'org.webkit:android-jsc:+'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to enable the Hermes VM.
|
||||||
|
*
|
||||||
|
* This should be set on project.ext.react and mirrored here. If it is not set
|
||||||
|
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
|
||||||
|
* and the benefits of using Hermes will therefore be sharply reduced.
|
||||||
|
*/
|
||||||
|
def enableHermes = project.ext.react.get("enableHermes", false);
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId 'com.aurailus.focuscompanion'
|
||||||
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0.0"
|
||||||
|
|
||||||
|
ndk {
|
||||||
|
abiFilters 'armeabi-v7a'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
splits {
|
||||||
|
abi {
|
||||||
|
reset()
|
||||||
|
enable enableSeparateBuildPerCPUArchitecture
|
||||||
|
universalApk false // If true, also generate a universal APK
|
||||||
|
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signingConfigs {
|
||||||
|
debug {
|
||||||
|
storeFile file('debug.keystore')
|
||||||
|
storePassword 'android'
|
||||||
|
keyAlias 'androiddebugkey'
|
||||||
|
keyPassword 'android'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
debug {
|
||||||
|
signingConfig signingConfigs.debug
|
||||||
|
}
|
||||||
|
release {
|
||||||
|
// Caution! In production, you need to generate your own keystore file.
|
||||||
|
// see https://reactnative.dev/docs/signed-apk-android.
|
||||||
|
signingConfig signingConfigs.debug
|
||||||
|
minifyEnabled enableProguardInReleaseBuilds
|
||||||
|
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// applicationVariants are e.g. debug, release
|
||||||
|
applicationVariants.all { variant ->
|
||||||
|
variant.outputs.each { output ->
|
||||||
|
// For each separate APK per architecture, set a unique version code as described here:
|
||||||
|
// https://developer.android.com/studio/build/configure-apk-splits.html
|
||||||
|
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
|
||||||
|
def abi = output.getFilter(OutputFile.ABI)
|
||||||
|
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||||
|
output.versionCodeOverride =
|
||||||
|
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||||
|
//noinspection GradleDynamicVersion
|
||||||
|
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||||
|
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||||
|
implementation "com.android.support:appcompat-v7:$supportLibVersion"
|
||||||
|
implementation "com.android.support:recyclerview-v7:$supportLibVersion"
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
||||||
|
exclude group:'com.facebook.fbjni'
|
||||||
|
}
|
||||||
|
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
|
||||||
|
exclude group:'com.facebook.flipper'
|
||||||
|
exclude group:'com.squareup.okhttp3', module:'okhttp'
|
||||||
|
}
|
||||||
|
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
|
||||||
|
exclude group:'com.facebook.flipper'
|
||||||
|
}
|
||||||
|
addUnimodulesDependencies()
|
||||||
|
|
||||||
|
if (enableHermes) {
|
||||||
|
def hermesPath = "../../node_modules/hermes-engine/android/";
|
||||||
|
debugImplementation files(hermesPath + "hermes-debug.aar")
|
||||||
|
releaseImplementation files(hermesPath + "hermes-release.aar")
|
||||||
|
} else {
|
||||||
|
implementation jscFlavor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run this once to be able to run the application with BUCK
|
||||||
|
// puts all compile dependencies into folder libs for BUCK to use
|
||||||
|
task copyDownloadableDepsToLibs(type: Copy) {
|
||||||
|
from configurations.compile into 'libs'
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
19
companion/android/app/build_defs.bzl
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
"""Helper definitions to glob .aar and .jar targets"""
|
||||||
|
|
||||||
|
def create_aar_targets(aarfiles):
|
||||||
|
for aarfile in aarfiles:
|
||||||
|
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
|
||||||
|
lib_deps.append(":" + name)
|
||||||
|
android_prebuilt_aar(
|
||||||
|
name = name,
|
||||||
|
aar = aarfile,
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_jar_targets(jarfiles):
|
||||||
|
for jarfile in jarfiles:
|
||||||
|
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
|
||||||
|
lib_deps.append(":" + name)
|
||||||
|
prebuilt_jar(
|
||||||
|
name = name,
|
||||||
|
binary_jar = jarfile,
|
||||||
|
)
|
BIN
companion/android/app/debug.keystore
Normal file
10
companion/android/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
|
# directive in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
7
companion/android/app/src/debug/AndroidManifest.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
|
|
||||||
|
<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" />
|
||||||
|
</manifest>
|
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||||
|
* directory of this source tree.
|
||||||
|
*/
|
||||||
|
package com.aurailus.focuscompanion;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||||
|
import com.facebook.flipper.android.utils.FlipperUtils;
|
||||||
|
import com.facebook.flipper.core.FlipperClient;
|
||||||
|
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
|
||||||
|
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
|
||||||
|
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
|
||||||
|
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
|
||||||
|
import com.facebook.react.ReactInstanceManager;
|
||||||
|
import com.facebook.react.bridge.ReactContext;
|
||||||
|
import com.facebook.react.modules.network.NetworkingModule;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
|
||||||
|
public class ReactNativeFlipper {
|
||||||
|
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||||
|
if (FlipperUtils.shouldEnableFlipper(context)) {
|
||||||
|
final FlipperClient client = AndroidFlipperClient.getInstance(context);
|
||||||
|
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
|
||||||
|
client.addPlugin(new ReactFlipperPlugin());
|
||||||
|
client.addPlugin(new DatabasesFlipperPlugin(context));
|
||||||
|
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
|
||||||
|
client.addPlugin(CrashReporterPlugin.getInstance());
|
||||||
|
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
|
||||||
|
NetworkingModule.setCustomClientBuilder(
|
||||||
|
new NetworkingModule.CustomClientBuilder() {
|
||||||
|
@Override
|
||||||
|
public void apply(OkHttpClient.Builder builder) {
|
||||||
|
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
client.addPlugin(networkFlipperPlugin);
|
||||||
|
client.start();
|
||||||
|
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
|
||||||
|
// Hence we run if after all native modules have been initialized
|
||||||
|
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
||||||
|
if (reactContext == null) {
|
||||||
|
reactInstanceManager.addReactInstanceEventListener(
|
||||||
|
new ReactInstanceManager.ReactInstanceEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onReactContextInitialized(ReactContext reactContext) {
|
||||||
|
reactInstanceManager.removeReactInstanceEventListener(this);
|
||||||
|
reactContext.runOnNativeModulesQueueThread(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
client.addPlugin(new FrescoFlipperPlugin());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
client.addPlugin(new FrescoFlipperPlugin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
companion/android/app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.aurailus.focuscompanion">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
|
<uses-permission android:name="android.permission.READ_CALENDAR" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:usesCleartextTraffic="true">
|
||||||
|
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
|
||||||
|
<meta-data android:name="expo.modules.updates.EXPO_SDK_VERSION" android:value="42.0.0"/>
|
||||||
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
||||||
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
|
||||||
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://exp.host/@anonymous/FocusCompanion"/>
|
||||||
|
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:screenOrientation="portrait">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
<data android:scheme="myapp"/>
|
||||||
|
<data android:scheme="com.aurailus.focuscompanion"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
958
companion/android/app/src/main/assets/index.android.bundle
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package com.aurailus.focuscompanion
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.provider.CalendarContract
|
||||||
|
import com.facebook.react.bridge.*
|
||||||
|
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
|
||||||
|
|
||||||
|
class CalendarModule(var ctx: ReactApplicationContext): ReactContextBaseJavaModule(ctx) {
|
||||||
|
fun getEnabled(): Set<String> {
|
||||||
|
val prefs = ctx.getSharedPreferences(
|
||||||
|
ctx.getString(R.string.preferences_key), Context.MODE_PRIVATE)
|
||||||
|
return prefs.getStringSet("calendars_enabled", HashSet())!!
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod fun getCalendars(promise: Promise) {
|
||||||
|
val cur: Cursor = ctx.contentResolver?.query(
|
||||||
|
CalendarContract.Calendars.CONTENT_URI, arrayOf(
|
||||||
|
CalendarContract.Calendars._ID,
|
||||||
|
CalendarContract.Calendars.CALENDAR_DISPLAY_NAME,
|
||||||
|
CalendarContract.Calendars.CALENDAR_COLOR
|
||||||
|
),
|
||||||
|
null, null, null
|
||||||
|
) ?: return promise.reject("Failed to get calendars.")
|
||||||
|
|
||||||
|
val enabled = getEnabled()
|
||||||
|
val arr = Arguments.createArray()
|
||||||
|
while (cur.moveToNext()) {
|
||||||
|
val data = Arguments.createMap()
|
||||||
|
data.putString("id", cur.getString(0))
|
||||||
|
data.putString("title", cur.getString(1))
|
||||||
|
|
||||||
|
val colorInt = cur.getInt(2)
|
||||||
|
val color = String.format("#%06X", 0xFFFFFF and colorInt)
|
||||||
|
data.putString("color", color)
|
||||||
|
|
||||||
|
data.putBoolean("enabled", enabled.contains(data.getString("id")))
|
||||||
|
|
||||||
|
arr.pushMap(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
cur.close()
|
||||||
|
promise.resolve(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod fun setCalendars(enabledArr: ReadableArray, promise: Promise) {
|
||||||
|
val enabled = HashSet<String>()
|
||||||
|
for (i in 0 until enabledArr.size())
|
||||||
|
enabledArr.getString(i)?.let { enabled.add(it) }
|
||||||
|
|
||||||
|
val prefs = ctx.getSharedPreferences(
|
||||||
|
ctx.getString(R.string.preferences_key), Context.MODE_PRIVATE)
|
||||||
|
prefs.edit().putStringSet("calendars_enabled", enabled).apply()
|
||||||
|
|
||||||
|
promise.resolve(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getName(): String {
|
||||||
|
return "CalendarModule"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.aurailus.focuscompanion
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.os.Bundle
|
||||||
|
import com.facebook.react.ReactActivity
|
||||||
|
import com.facebook.react.ReactActivityDelegate
|
||||||
|
import com.facebook.react.ReactRootView
|
||||||
|
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView
|
||||||
|
import expo.modules.splashscreen.SplashScreenImageResizeMode
|
||||||
|
import expo.modules.splashscreen.singletons.SplashScreen.show
|
||||||
|
|
||||||
|
class MainActivity : ReactActivity() {
|
||||||
|
// Added automatically by Expo Config
|
||||||
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
|
super.onConfigurationChanged(newConfig)
|
||||||
|
val intent = Intent("onConfigurationChanged")
|
||||||
|
intent.putExtra("newConfig", newConfig)
|
||||||
|
sendBroadcast(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
// Set the theme to AppTheme BEFORE onCreate to support
|
||||||
|
// coloring the background, status bar, and navigation bar.
|
||||||
|
// This is required for expo-splash-screen.
|
||||||
|
// setTheme(R.style.AppTheme)
|
||||||
|
super.onCreate(null)
|
||||||
|
// @generated begin expo-splash-screen-mainActivity-onCreate-show-splash - expo prebuild (DO NOT MODIFY) sync-8915a20732e7fda227585f9b6ef0d38bef4fbbbe
|
||||||
|
show(this, SplashScreenImageResizeMode.CONTAIN, ReactRootView::class.java, false)
|
||||||
|
// @generated end expo-splash-screen-mainActivity-onCreate-show-splash
|
||||||
|
// SplashScreen.show(...) has to be called after super.onCreate(...)
|
||||||
|
// Below line is handled by '@expo/configure-splash-screen' command and it's discouraged to modify it manually
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createReactActivityDelegate(): ReactActivityDelegate {
|
||||||
|
return object : ReactActivityDelegate(this, "main") {
|
||||||
|
override fun createRootView(): ReactRootView {
|
||||||
|
return RNGestureHandlerEnabledRootView(this@MainActivity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
package com.aurailus.focuscompanion
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import com.aurailus.focuscompanion.generated.BasePackageList
|
||||||
|
import com.facebook.react.PackageList
|
||||||
|
import com.facebook.react.ReactApplication
|
||||||
|
import com.facebook.react.ReactInstanceManager
|
||||||
|
import com.facebook.react.ReactNativeHost
|
||||||
|
import com.facebook.react.ReactPackage
|
||||||
|
import com.facebook.react.bridge.JSIModulePackage
|
||||||
|
import com.facebook.soloader.SoLoader
|
||||||
|
import com.swmansion.reanimated.ReanimatedJSIModulePackage
|
||||||
|
import expo.modules.updates.UpdatesController
|
||||||
|
import java.lang.reflect.InvocationTargetException
|
||||||
|
import org.unimodules.adapters.react.ModuleRegistryAdapter
|
||||||
|
import org.unimodules.adapters.react.ReactModuleRegistryProvider
|
||||||
|
|
||||||
|
class MainApplication : Application(), ReactApplication {
|
||||||
|
private val mModuleRegistryProvider = ReactModuleRegistryProvider(
|
||||||
|
BasePackageList().packageList
|
||||||
|
)
|
||||||
|
|
||||||
|
private val mReactNativeHost: ReactNativeHost = object : ReactNativeHost(this) {
|
||||||
|
override fun getUseDeveloperSupport(): Boolean {
|
||||||
|
return BuildConfig.DEBUG
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPackages(): List<ReactPackage> {
|
||||||
|
val packages: MutableList<ReactPackage> = PackageList(this).packages
|
||||||
|
packages.add(ModuleRegistryAdapter(mModuleRegistryProvider))
|
||||||
|
packages.add(MyAppPackage())
|
||||||
|
return packages
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getJSMainModuleName(): String {
|
||||||
|
return "index"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getJSIModulePackage(): JSIModulePackage {
|
||||||
|
return ReanimatedJSIModulePackage()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getJSBundleFile(): String? {
|
||||||
|
return if (BuildConfig.DEBUG) {
|
||||||
|
super.getJSBundleFile()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UpdatesController.getInstance().launchAssetFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBundleAssetName(): String? {
|
||||||
|
return if (BuildConfig.DEBUG) {
|
||||||
|
super.getBundleAssetName()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UpdatesController.getInstance().bundleAssetName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getReactNativeHost(): ReactNativeHost {
|
||||||
|
return mReactNativeHost
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
SoLoader.init(this, false)
|
||||||
|
if (!BuildConfig.DEBUG) UpdatesController.initialize(this)
|
||||||
|
initializeFlipper(this, reactNativeHost.reactInstanceManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
|
||||||
|
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param reactInstanceManager
|
||||||
|
*/
|
||||||
|
private fun initializeFlipper(
|
||||||
|
context: Context, reactInstanceManager: ReactInstanceManager
|
||||||
|
) {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
* We use reflection here to pick up the class that initializes Flipper,
|
||||||
|
* since Flipper library is not available in release mode
|
||||||
|
*/
|
||||||
|
val aClass = Class.forName("com.aurailus.focuscompanion.ReactNativeFlipper")
|
||||||
|
aClass.getMethod(
|
||||||
|
"initializeFlipper",
|
||||||
|
Context::class.java,
|
||||||
|
ReactInstanceManager::class.java
|
||||||
|
).invoke(null, context, reactInstanceManager)
|
||||||
|
} catch (e: ClassNotFoundException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
} catch (e: NoSuchMethodException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
} catch (e: IllegalAccessException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
} catch (e: InvocationTargetException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.aurailus.focuscompanion
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import com.facebook.react.ReactPackage
|
||||||
|
import com.facebook.react.bridge.NativeModule
|
||||||
|
import com.facebook.react.bridge.ReactApplicationContext
|
||||||
|
import com.facebook.react.uimanager.ReactShadowNode
|
||||||
|
import com.facebook.react.uimanager.ViewManager
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class MyAppPackage: ReactPackage {
|
||||||
|
override fun createViewManagers(reactContext: ReactApplicationContext): MutableList<ViewManager<View, ReactShadowNode<*>>> {
|
||||||
|
return Collections.emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createNativeModules(reactContext: ReactApplicationContext): MutableList<NativeModule> {
|
||||||
|
val modules = ArrayList<NativeModule>()
|
||||||
|
modules.add(CalendarModule(reactContext))
|
||||||
|
return modules
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.aurailus.focuscompanion.generated;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import org.unimodules.core.interfaces.Package;
|
||||||
|
|
||||||
|
public class BasePackageList {
|
||||||
|
public List<Package> getPackageList() {
|
||||||
|
return Arrays.<Package>asList(
|
||||||
|
new expo.modules.application.ApplicationPackage(),
|
||||||
|
new expo.modules.constants.ConstantsPackage(),
|
||||||
|
new expo.modules.errorrecovery.ErrorRecoveryPackage(),
|
||||||
|
new expo.modules.filesystem.FileSystemPackage(),
|
||||||
|
new expo.modules.font.FontLoaderPackage(),
|
||||||
|
new expo.modules.imageloader.ImageLoaderPackage(),
|
||||||
|
new expo.modules.keepawake.KeepAwakePackage(),
|
||||||
|
new expo.modules.splashscreen.SplashScreenPackage(),
|
||||||
|
new expo.modules.updates.UpdatesPackage(),
|
||||||
|
new expo.modules.webbrowser.WebBrowserPackage()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- <background android:drawable="#000000"/>-->
|
||||||
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- <background android:drawable="#000000"/>-->
|
||||||
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
BIN
companion/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 4.0 KiB |
BIN
companion/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.6 KiB |
BIN
companion/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 5.5 KiB |
BIN
companion/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 12 KiB |
5
companion/android/app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">FocusCompanion</string>
|
||||||
|
<string name="preferences_key">com.aurailus.focuscompanion.PREFERENCE_FILE_KEY</string>
|
||||||
|
</resources>
|
42
companion/android/build.gradle
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
ext {
|
||||||
|
buildToolsVersion = "29.0.3"
|
||||||
|
minSdkVersion = 21
|
||||||
|
compileSdkVersion = 30
|
||||||
|
targetSdkVersion = 30
|
||||||
|
kotlin_version = "1.5.30"
|
||||||
|
supportLibVersion = "25.3.0"
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath("com.android.tools.build:gradle:4.1.0")
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
|
||||||
|
|
||||||
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
// in the individual module build.gradle files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
maven {
|
||||||
|
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||||
|
url("$rootDir/../node_modules/react-native/android")
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
// Android JSC is installed from npm
|
||||||
|
url("$rootDir/../node_modules/jsc-android/dist")
|
||||||
|
}
|
||||||
|
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
maven { url 'https://www.jitpack.io' }
|
||||||
|
}
|
||||||
|
}
|
33
companion/android/gradle.properties
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||||
|
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||||
|
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
# org.gradle.parallel=true
|
||||||
|
|
||||||
|
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||||
|
# Android operating system, and which are packaged with your app's APK
|
||||||
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
|
android.useAndroidX=true
|
||||||
|
|
||||||
|
# Automatically convert third-party libraries to use AndroidX
|
||||||
|
android.enableJetifier=true
|
||||||
|
|
||||||
|
# Version of flipper SDK to use with React Native
|
||||||
|
FLIPPER_VERSION=0.54.0
|
||||||
|
|
||||||
|
# The hosted JavaScript engine
|
||||||
|
# Supported values: expo.jsEngine = "hermes" | "jsc"
|
||||||
|
expo.jsEngine=jsc
|
BIN
companion/android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
companion/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Sat Sep 11 23:20:59 PDT 2021
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
183
companion/android/gradlew
vendored
Executable file
@ -0,0 +1,183 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
# https://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.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=`expr $i + 1`
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
103
companion/android/gradlew.bat
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
9
companion/android/settings.gradle
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
rootProject.name = 'FocusCompanion'
|
||||||
|
|
||||||
|
apply from: '../node_modules/react-native-unimodules/gradle.groovy'
|
||||||
|
includeUnimodulesProjects()
|
||||||
|
|
||||||
|
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle");
|
||||||
|
applyNativeModulesSettingsGradle(settings)
|
||||||
|
|
||||||
|
include ':app'
|
36
companion/app.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"expo": {
|
||||||
|
"name": "FocusCompanion",
|
||||||
|
"slug": "FocusCompanion",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"orientation": "portrait",
|
||||||
|
"icon": "./assets/images/icon.png",
|
||||||
|
"scheme": "myapp",
|
||||||
|
"userInterfaceStyle": "automatic",
|
||||||
|
"splash": {
|
||||||
|
"image": "./assets/images/splash.png",
|
||||||
|
"resizeMode": "contain",
|
||||||
|
"backgroundColor": "#ffffff"
|
||||||
|
},
|
||||||
|
"updates": {
|
||||||
|
"fallbackToCacheTimeout": 0
|
||||||
|
},
|
||||||
|
"assetBundlePatterns": [
|
||||||
|
"**/*"
|
||||||
|
],
|
||||||
|
"ios": {
|
||||||
|
"supportsTablet": true,
|
||||||
|
"bundleIdentifier": "com.aurailus.focuscompanion"
|
||||||
|
},
|
||||||
|
"android": {
|
||||||
|
"adaptiveIcon": {
|
||||||
|
"foregroundImage": "./assets/images/adaptive-icon.png",
|
||||||
|
"backgroundColor": "#ffffff"
|
||||||
|
},
|
||||||
|
"package": "com.aurailus.focuscompanion"
|
||||||
|
},
|
||||||
|
"web": {
|
||||||
|
"favicon": "./assets/images/favicon.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
companion/assets/fonts/SpaceMono-Regular.ttf
Executable file
BIN
companion/assets/images/adaptive-icon.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
companion/assets/images/favicon.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
companion/assets/images/icon.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
companion/assets/images/splash.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
companion/assets/images/watch.png
Normal file
After Width: | Height: | Size: 46 KiB |
7
companion/babel.config.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module.exports = function(api) {
|
||||||
|
api.cache(true);
|
||||||
|
return {
|
||||||
|
presets: ['babel-preset-expo'],
|
||||||
|
plugins: ['react-native-reanimated/plugin'],
|
||||||
|
};
|
||||||
|
};
|
9
companion/index.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import 'react-native-gesture-handler';
|
||||||
|
import { registerRootComponent } from 'expo';
|
||||||
|
|
||||||
|
import App from './src/App';
|
||||||
|
|
||||||
|
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
|
||||||
|
// It also ensures that whether you load the app in Expo Go or in a native build,
|
||||||
|
// the environment is set up appropriately
|
||||||
|
registerRootComponent(App);
|
4
companion/metro.config.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// Learn more https://docs.expo.io/guides/customizing-metro
|
||||||
|
const { getDefaultConfig } = require('expo/metro-config');
|
||||||
|
|
||||||
|
module.exports = getDefaultConfig(__dirname);
|
15753
companion/package-lock.json
generated
Normal file
43
companion/package.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-native start",
|
||||||
|
"android": "react-native run-android",
|
||||||
|
"android-prebuild": "npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res/"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@expo/vector-icons": "^12.0.0",
|
||||||
|
"@react-navigation/bottom-tabs": "^6.0.5",
|
||||||
|
"@react-navigation/material-bottom-tabs": "^6.0.5",
|
||||||
|
"@react-navigation/native": "^6.0.2",
|
||||||
|
"@react-navigation/native-stack": "^6.1.0",
|
||||||
|
"expo": "~42.0.1",
|
||||||
|
"expo-asset": "~8.3.2",
|
||||||
|
"expo-constants": "~11.0.1",
|
||||||
|
"expo-font": "~9.2.1",
|
||||||
|
"expo-linking": "~2.3.1",
|
||||||
|
"expo-splash-screen": "~0.11.2",
|
||||||
|
"expo-status-bar": "~1.0.4",
|
||||||
|
"expo-web-browser": "~9.2.0",
|
||||||
|
"react": "16.13.1",
|
||||||
|
"react-dom": "16.13.1",
|
||||||
|
"react-native": "~0.63.4",
|
||||||
|
"react-native-gesture-handler": "~1.10.2",
|
||||||
|
"react-native-paper": "^4.9.2",
|
||||||
|
"react-native-reanimated": "~2.2.0",
|
||||||
|
"react-native-safe-area-context": "3.2.0",
|
||||||
|
"react-native-screens": "~3.4.0",
|
||||||
|
"react-native-svg": "^12.1.1",
|
||||||
|
"react-native-web": "~0.13.12",
|
||||||
|
"expo-updates": "~0.8.1",
|
||||||
|
"react-native-unimodules": "~0.14.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.9.0",
|
||||||
|
"@types/react": "~16.9.35",
|
||||||
|
"@types/react-native": "~0.63.2",
|
||||||
|
"typescript": "~4.0.0"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"name": "focuscompanion",
|
||||||
|
"version": "0.0.1"
|
||||||
|
}
|
58
companion/src/App.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { StatusBar } from 'expo-status-bar';
|
||||||
|
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||||
|
import { View, Text, Image, StyleSheet, } from 'react-native';
|
||||||
|
|
||||||
|
import Navigation from './Navigation';
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<SafeAreaProvider style={{ backgroundColor: '#0c1014' }}>
|
||||||
|
<StatusBar style='light'/>
|
||||||
|
<View style={styles.top}>
|
||||||
|
<View style={styles.topImageWrap}>
|
||||||
|
<Image source={require('../assets/images/watch.png')} style={styles.topImage}/>
|
||||||
|
</View>
|
||||||
|
<View style={styles.titleWrap}>
|
||||||
|
<Text style={styles.title}>Focus Watchface</Text>
|
||||||
|
<Text style={styles.subtitle}>Companion App</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{/* <View style={{ borderRadius: 12, flexGrow: 1, overflow: 'hidden' }}> */}
|
||||||
|
<Navigation/>
|
||||||
|
{/* </View> */}
|
||||||
|
</SafeAreaProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
titleWrap: {
|
||||||
|
padding: 24,
|
||||||
|
justifyContent: 'center'
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
color: '#cde',
|
||||||
|
fontSize: 22,
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
color: '#789',
|
||||||
|
fontSize: 18
|
||||||
|
},
|
||||||
|
top: {
|
||||||
|
paddingLeft: 16,
|
||||||
|
paddingTop: 48,
|
||||||
|
paddingBottom: 16,
|
||||||
|
flexDirection: 'row'
|
||||||
|
},
|
||||||
|
topImageWrap: {
|
||||||
|
borderRadius: 9999,
|
||||||
|
backgroundColor: '#181f24',
|
||||||
|
padding: 4,
|
||||||
|
flexGrow: 0
|
||||||
|
},
|
||||||
|
topImage: {
|
||||||
|
width: 128,
|
||||||
|
height: 128,
|
||||||
|
borderRadius: 9999
|
||||||
|
}
|
||||||
|
});
|
80
companion/src/EditScreenInfo.tsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// import * as WebBrowser from 'expo-web-browser';
|
||||||
|
// import React from 'react';
|
||||||
|
// import { StyleSheet, TouchableOpacity } from 'react-native';
|
||||||
|
|
||||||
|
// import Colors from '../constants/Colors';
|
||||||
|
// import { MonoText } from './StyledText';
|
||||||
|
// import { Text, View } from './Themed';
|
||||||
|
|
||||||
|
// export default function EditScreenInfo({ path }: { path: string }) {
|
||||||
|
// return (
|
||||||
|
// <View>
|
||||||
|
// <View style={styles.getStartedContainer}>
|
||||||
|
// <Text
|
||||||
|
// style={styles.getStartedText}
|
||||||
|
// lightColor="rgba(0,0,0,0.8)"
|
||||||
|
// darkColor="rgba(255,255,255,0.8)">
|
||||||
|
// Open up the code for this screen:
|
||||||
|
// </Text>
|
||||||
|
|
||||||
|
// <View
|
||||||
|
// style={[styles.codeHighlightContainer, styles.homeScreenFilename]}
|
||||||
|
// darkColor="rgba(255,255,255,0.05)"
|
||||||
|
// lightColor="rgba(0,0,0,0.05)">
|
||||||
|
// <MonoText>{path}</MonoText>
|
||||||
|
// </View>
|
||||||
|
|
||||||
|
// <Text
|
||||||
|
// style={styles.getStartedText}
|
||||||
|
// lightColor="rgba(0,0,0,0.8)"
|
||||||
|
// darkColor="rgba(255,255,255,0.8)">
|
||||||
|
// Change any of the text, save the file, and your app will automatically update.
|
||||||
|
// </Text>
|
||||||
|
// </View>
|
||||||
|
|
||||||
|
// <View style={styles.helpContainer}>
|
||||||
|
// <TouchableOpacity onPress={handleHelpPress} style={styles.helpLink}>
|
||||||
|
// <Text style={styles.helpLinkText} lightColor={Colors.light.tint}>
|
||||||
|
// Tap here if your app doesn't automatically update after making changes
|
||||||
|
// </Text>
|
||||||
|
// </TouchableOpacity>
|
||||||
|
// </View>
|
||||||
|
// </View>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function handleHelpPress() {
|
||||||
|
// WebBrowser.openBrowserAsync(
|
||||||
|
// 'https://docs.expo.io/get-started/create-a-new-app/#opening-the-app-on-your-phonetablet'
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const styles = StyleSheet.create({
|
||||||
|
// getStartedContainer: {
|
||||||
|
// alignItems: 'center',
|
||||||
|
// marginHorizontal: 50,
|
||||||
|
// },
|
||||||
|
// homeScreenFilename: {
|
||||||
|
// marginVertical: 7,
|
||||||
|
// },
|
||||||
|
// codeHighlightContainer: {
|
||||||
|
// borderRadius: 3,
|
||||||
|
// paddingHorizontal: 4,
|
||||||
|
// },
|
||||||
|
// getStartedText: {
|
||||||
|
// fontSize: 17,
|
||||||
|
// lineHeight: 24,
|
||||||
|
// textAlign: 'center',
|
||||||
|
// },
|
||||||
|
// helpContainer: {
|
||||||
|
// marginTop: 15,
|
||||||
|
// marginHorizontal: 20,
|
||||||
|
// alignItems: 'center',
|
||||||
|
// },
|
||||||
|
// helpLink: {
|
||||||
|
// paddingVertical: 15,
|
||||||
|
// },
|
||||||
|
// helpLinkText: {
|
||||||
|
// textAlign: 'center',
|
||||||
|
// },
|
||||||
|
// });
|
79
companion/src/Icon.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import Svg, { Path, Circle } from 'react-native-svg';
|
||||||
|
|
||||||
|
/** The default icon size, if none is specified. */
|
||||||
|
export const DEFAULT_ICON_SIZE = 24;
|
||||||
|
|
||||||
|
/** The valid icon names that can be provided to <Icon/> */
|
||||||
|
export type IconName = 'calendar' | 'theme' | 'heart';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
width?: number | string;
|
||||||
|
height?: number | string;
|
||||||
|
color?: string | undefined;
|
||||||
|
colorPrimary?: string | undefined;
|
||||||
|
colorSecondary?: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the derived values from an IconProps. */
|
||||||
|
function useIconDerivedProps(props: IconProps) {
|
||||||
|
const width = props.width ?? DEFAULT_ICON_SIZE;
|
||||||
|
const height = props.height ?? width;
|
||||||
|
const colorSecondary = props.color ?? props.colorPrimary ?? '#fff';
|
||||||
|
const colorPrimary = props.colorSecondary ??
|
||||||
|
(colorSecondary.length === 4 ? colorSecondary + '9' : colorSecondary + '99');
|
||||||
|
|
||||||
|
return { width, height, colorPrimary, colorSecondary };
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Below are components to render specific icons. */
|
||||||
|
|
||||||
|
export function Calendar(props: IconProps) {
|
||||||
|
const { width, height, colorPrimary, colorSecondary } = useIconDerivedProps(props);
|
||||||
|
return (
|
||||||
|
<Svg viewBox='0 0 24 24' width={width} height={height}>
|
||||||
|
<Path fill={colorPrimary} d='M5 4h14a2 2 0 0 1 2 2v13a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2zm0 5v10h14V9H5z'/>
|
||||||
|
<Path fill={colorSecondary} d='M13 13h3v3h-3v-3zM7 2a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V3a1 1 0 0 1 1-1zm10 0a1 1 0 0 1 1 1v3a1 1 0 0 1-2 0V3a1 1 0 0 1 1-1z'/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Theme(props: IconProps) {
|
||||||
|
const { width, height, colorPrimary, colorSecondary } = useIconDerivedProps(props);
|
||||||
|
return (
|
||||||
|
<Svg viewBox='0 0 24 24' width={width} height={height}>
|
||||||
|
<Path fill={colorPrimary} d='M9 22c.19-.14.37-.3.54-.46L17.07 14H20a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H9zM4 2h4a2 2 0 0 1 2 2v14a4 4 0 1 1-8 0V4c0-1.1.9-2 2-2zm2 17.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z'/>
|
||||||
|
<Path fill={colorSecondary} d='M11 18.66V7.34l2.07-2.07a2 2 0 0 1 2.83 0l2.83 2.83a2 2 0 0 1 0 2.83L11 18.66z'/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Heart(props: IconProps) {
|
||||||
|
const { width, height, colorPrimary, colorSecondary } = useIconDerivedProps(props);
|
||||||
|
return (
|
||||||
|
<Svg viewBox='0 0 24 24' width={width} height={height}>
|
||||||
|
<Circle fill={colorPrimary} cx="12" cy="12" r="10"/>
|
||||||
|
<Path fill={colorSecondary} d="M12.88 8.88a3 3 0 1 1 4.24 4.24l-4.41 4.42a1 1 0 0 1-1.42 0l-4.41-4.42a3 3 0 1 1 4.24-4.24l.88.88.88-.88z"/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A mapping of icon name to component. */
|
||||||
|
const ICON_COMPONENTS: Record<IconName, React.FunctionComponent<IconProps>> = {
|
||||||
|
'calendar': Calendar,
|
||||||
|
'theme': Theme,
|
||||||
|
'heart': Heart
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Props extends IconProps {
|
||||||
|
icon: IconName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders whatever icon is provided in the `icon` prop.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function Icon(props: Props) {
|
||||||
|
const Component = ICON_COMPONENTS[props.icon];
|
||||||
|
return <Component {...props}/>;
|
||||||
|
}
|
167
companion/src/Navigation.tsx
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||||
|
import { NavigationContainer, DarkTheme } from '@react-navigation/native';
|
||||||
|
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||||
|
|
||||||
|
import Icon, { IconName } from './Icon';
|
||||||
|
import { RootStackParamList, RootTabParamList } from '../types';
|
||||||
|
import { ModalScreen, CalendarsScreen, ThemesScreen, AboutScreen } from './screens';
|
||||||
|
import Animated, { withTiming, useAnimatedStyle, Easing } from 'react-native-reanimated';
|
||||||
|
|
||||||
|
import { LinkingOptions } from '@react-navigation/native';
|
||||||
|
import * as Linking from 'expo-linking';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The linking for the different screens.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const LINKING: LinkingOptions<RootStackParamList> = {
|
||||||
|
prefixes: [Linking.makeUrl('/')],
|
||||||
|
config: {
|
||||||
|
screens: {
|
||||||
|
Root: {
|
||||||
|
screens: {
|
||||||
|
Calendars: {
|
||||||
|
screens: {
|
||||||
|
CalendarsScreen: 'calendar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Theme: {
|
||||||
|
screens: {
|
||||||
|
ThemeScreen: 'theme',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
About: {
|
||||||
|
screens: {
|
||||||
|
AboutScreen: 'about',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Modal: 'modal',
|
||||||
|
NotFound: '*',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Displays the active screen based on the linking. */
|
||||||
|
const Stack = createNativeStackNavigator<RootStackParamList>();
|
||||||
|
|
||||||
|
const NAVIGATION_THEME = {
|
||||||
|
dark: true,
|
||||||
|
colors: {
|
||||||
|
primary: 'white',
|
||||||
|
background: 'transparent',
|
||||||
|
card: '#0c1014',
|
||||||
|
text: 'white',
|
||||||
|
border: 'transparent',
|
||||||
|
notification: 'red'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the navigation and active screen.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function Navigation() {
|
||||||
|
return (
|
||||||
|
<NavigationContainer linking={LINKING} theme={NAVIGATION_THEME}>
|
||||||
|
<Stack.Navigator>
|
||||||
|
<Stack.Screen name='Root' component={BottomTabNavigator} options={{ headerShown: false }} />
|
||||||
|
<Stack.Group screenOptions={{ presentation: 'modal' }}>
|
||||||
|
<Stack.Screen name='Modal' component={ModalScreen} />
|
||||||
|
</Stack.Group>
|
||||||
|
</Stack.Navigator>
|
||||||
|
</NavigationContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Displays a tab bar at the bottom of the screen. */
|
||||||
|
const BottomTab = createBottomTabNavigator<RootTabParamList>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the bottom navigation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function BottomTabNavigator() {
|
||||||
|
return (
|
||||||
|
<BottomTab.Navigator
|
||||||
|
initialRouteName='Calendars'
|
||||||
|
screenOptions={{
|
||||||
|
headerShown: false,
|
||||||
|
tabBarStyle: { height: 64 }
|
||||||
|
}}>
|
||||||
|
<BottomTab.Screen
|
||||||
|
name='Calendars'
|
||||||
|
component={CalendarsScreen}
|
||||||
|
options={{
|
||||||
|
title: 'Calendars',
|
||||||
|
tabBarIcon: ({ focused }) => <TabBarIcon focused={focused} icon='calendar'/>,
|
||||||
|
tabBarLabel: ({ focused }) => <TabBarLabel focused={focused} title='Calendars'/>
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<BottomTab.Screen
|
||||||
|
name='Theme'
|
||||||
|
component={ThemesScreen}
|
||||||
|
options={{
|
||||||
|
title: 'Theme',
|
||||||
|
tabBarIcon: ({ focused }) => <TabBarIcon focused={focused} icon='theme'/>,
|
||||||
|
tabBarLabel: ({ focused }) => <TabBarLabel focused={focused} title='Theme'/>
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<BottomTab.Screen
|
||||||
|
name='About'
|
||||||
|
component={AboutScreen}
|
||||||
|
options={{
|
||||||
|
title: 'About',
|
||||||
|
tabBarIcon: ({ focused }) => <TabBarIcon focused={focused} icon='heart'/>,
|
||||||
|
tabBarLabel: ({ focused }) => <TabBarLabel focused={focused} title='About'/>
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</BottomTab.Navigator>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handles animating tab bar labels. */
|
||||||
|
const ANIM_CONFIG = { duration: 100, easing: Easing.inOut(Easing.ease) };
|
||||||
|
|
||||||
|
interface TabBarIconProps {
|
||||||
|
icon: IconName;
|
||||||
|
focused: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the icon for a tab in the tab bar.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function TabBarIcon(props: TabBarIconProps) {
|
||||||
|
return (
|
||||||
|
<Icon icon={props.icon} width={28} height={28}
|
||||||
|
color={props.focused ? '#ffc9fb' : '#789'}
|
||||||
|
colorSecondary={props.focused ? '#c46abd' : '#345'}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TabBarLabelProps {
|
||||||
|
title: string;
|
||||||
|
focused: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the label for a tab in the tab bar.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function TabBarLabel(props: TabBarLabelProps) {
|
||||||
|
const style = useAnimatedStyle(() => ({
|
||||||
|
opacity: withTiming(props.focused ? 1 : 0, ANIM_CONFIG),
|
||||||
|
fontSize: withTiming(props.focused ? 11 : 8, ANIM_CONFIG),
|
||||||
|
marginTop: withTiming(props.focused ? -6 : -20, ANIM_CONFIG),
|
||||||
|
marginBottom: withTiming(props.focused ? 10 : 6, ANIM_CONFIG),
|
||||||
|
}), [ props.focused ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Animated.Text style={[{ color: '#ffe8fd', }, style]}>
|
||||||
|
{props.title}
|
||||||
|
</Animated.Text>
|
||||||
|
);
|
||||||
|
}
|
7
companion/src/StyledText.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// import * as React from 'react';
|
||||||
|
|
||||||
|
// import { Text, TextProps } from './Themed';
|
||||||
|
|
||||||
|
// export function MonoText(props: TextProps) {
|
||||||
|
// return <Text {...props} style={[props.style, { fontFamily: 'space-mono' }]} />;
|
||||||
|
// }
|
46
companion/src/Themed.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// /**
|
||||||
|
// * Learn more about Light and Dark modes:
|
||||||
|
// * https://docs.expo.io/guides/color-schemes/
|
||||||
|
// */
|
||||||
|
|
||||||
|
// import * as React from 'react';
|
||||||
|
// import { Text as DefaultText, View as DefaultView } from 'react-native';
|
||||||
|
|
||||||
|
// import Colors from '../constants/Colors';
|
||||||
|
// import useColorScheme from '../hooks/useColorScheme';
|
||||||
|
|
||||||
|
// export function useThemeColor(
|
||||||
|
// props: { light?: string; dark?: string },
|
||||||
|
// colorName: keyof typeof Colors.light & keyof typeof Colors.dark
|
||||||
|
// ) {
|
||||||
|
// const theme = useColorScheme();
|
||||||
|
// const colorFromProps = props[theme];
|
||||||
|
|
||||||
|
// if (colorFromProps) {
|
||||||
|
// return colorFromProps;
|
||||||
|
// } else {
|
||||||
|
// return Colors[theme][colorName];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// type ThemeProps = {
|
||||||
|
// lightColor?: string;
|
||||||
|
// darkColor?: string;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export type TextProps = ThemeProps & DefaultText['props'];
|
||||||
|
// export type ViewProps = ThemeProps & DefaultView['props'];
|
||||||
|
|
||||||
|
// export function Text(props: TextProps) {
|
||||||
|
// const { style, lightColor, darkColor, ...otherProps } = props;
|
||||||
|
// const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
|
||||||
|
|
||||||
|
// return <DefaultText style={[{ color }, style]} {...otherProps} />;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function View(props: ViewProps) {
|
||||||
|
// const { style, lightColor, darkColor, ...otherProps } = props;
|
||||||
|
// const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
|
||||||
|
|
||||||
|
// return <DefaultView style={[{ backgroundColor }, style]} {...otherProps} />;
|
||||||
|
// }
|
19
companion/src/constants/Colors.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// const tintColorLight = '#2f95dc';
|
||||||
|
// const tintColorDark = '#f00';
|
||||||
|
|
||||||
|
// export default {
|
||||||
|
// light: {
|
||||||
|
// text: '#000',
|
||||||
|
// background: '#fff',
|
||||||
|
// tint: tintColorLight,
|
||||||
|
// tabIconDefault: '#ccc',
|
||||||
|
// tabIconSelected: tintColorLight,
|
||||||
|
// },
|
||||||
|
// dark: {
|
||||||
|
// text: '#fff',
|
||||||
|
// background: '#000',
|
||||||
|
// tint: tintColorDark,
|
||||||
|
// tabIconDefault: '#ccc',
|
||||||
|
// tabIconSelected: tintColorDark,
|
||||||
|
// }
|
||||||
|
// };
|
12
companion/src/constants/Layout.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Dimensions } from 'react-native';
|
||||||
|
|
||||||
|
const width = Dimensions.get('window').width;
|
||||||
|
const height = Dimensions.get('window').height;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
window: {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
},
|
||||||
|
isSmallDevice: width < 375,
|
||||||
|
};
|
17
companion/src/native/Calendars.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { NativeModules } from 'react-native';
|
||||||
|
const { CalendarModule } = NativeModules;
|
||||||
|
|
||||||
|
export interface Calendar {
|
||||||
|
id: string;
|
||||||
|
color: string;
|
||||||
|
title: string;
|
||||||
|
enabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCalendars(): Promise<Calendar[]> {
|
||||||
|
return CalendarModule.getCalendars();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setCalendars(enabled: string[]): Promise<void> {
|
||||||
|
return CalendarModule.setCalendars(enabled);
|
||||||
|
}
|
39
companion/src/screens/AboutScreen.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { StyleSheet, View, Text, Linking } from 'react-native';
|
||||||
|
import { Heart } from '../Icon';
|
||||||
|
|
||||||
|
export default function AboutScreen() {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Heart width={96} colorPrimary='#ff91f7' colorSecondary='#542459'/>
|
||||||
|
<Text style={styles.title}>Like Focus?</Text>
|
||||||
|
<Text style={styles.description}>Check out my other projects on my{' '}
|
||||||
|
<Text style={styles.link} onPress={() => Linking.openURL('https://aurail.us/discord')}>Discord</Text>.</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
borderRadius: 16,
|
||||||
|
overflow: 'hidden',
|
||||||
|
backgroundColor: 'black',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center'
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
color: '#789',
|
||||||
|
marginBottom: 36
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
color: 'white',
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginVertical: 12
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
color: '#c46abd',
|
||||||
|
fontWeight: '700'
|
||||||
|
}
|
||||||
|
});
|
115
companion/src/screens/CalendarsScreen.tsx
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { StyleSheet, View, Pressable, Text, ScrollView, Switch, FlatList } from 'react-native';
|
||||||
|
import * as Calendars from '../native/Calendars';
|
||||||
|
|
||||||
|
interface CalendarItemProps {
|
||||||
|
color: string;
|
||||||
|
title: string;
|
||||||
|
enabled: boolean;
|
||||||
|
onSwitch: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSwitchColors(enabled: boolean) {
|
||||||
|
return {
|
||||||
|
background: enabled ? '#c46abd' : '#123',
|
||||||
|
foreground: enabled ? '#ffc9fb' : '#789'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function CalendarItem(props: CalendarItemProps) {
|
||||||
|
const [ switchColors, setSwitchColors ] =
|
||||||
|
React.useState<{ background: string; foreground: string }>(getSwitchColors(props.enabled));
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
setTimeout(() => {
|
||||||
|
if (controller.signal.aborted) return;
|
||||||
|
setSwitchColors(getSwitchColors(props.enabled));
|
||||||
|
}, 100);
|
||||||
|
return () => controller.abort();
|
||||||
|
}, [ props.enabled ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.item}>
|
||||||
|
<Pressable android_ripple={{ color: '#14181f' }} style={styles.itemPressable} onPress={props.onSwitch}>
|
||||||
|
<View style={[ styles.itemDot, { backgroundColor: props.color }]}/>
|
||||||
|
<Text style={[ styles.itemTitle, { color: props.enabled ? '#c0d8dd' : '#567' }]}>{props.title}</Text>
|
||||||
|
<Switch
|
||||||
|
trackColor={{ false: switchColors.background, true: switchColors.background }}
|
||||||
|
thumbColor={switchColors.foreground}
|
||||||
|
value={props.enabled} onValueChange={props.onSwitch}/>
|
||||||
|
</Pressable>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function CalendarsScreen() {
|
||||||
|
const [ calendars, setCalendars ] = React.useState<Calendars.Calendar[]>([]);
|
||||||
|
|
||||||
|
const refreshCalendars = async () => {
|
||||||
|
setCalendars((await Calendars.getCalendars()).sort((a, b) => a.title.localeCompare(b.title)));
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => void(refreshCalendars()), []);
|
||||||
|
|
||||||
|
const handleToggle = async (ind: number) => {
|
||||||
|
const newCalendars: Calendars.Calendar[] = JSON.parse(JSON.stringify(calendars));
|
||||||
|
newCalendars[ind].enabled = !newCalendars[ind].enabled;
|
||||||
|
setCalendars(newCalendars);
|
||||||
|
Calendars.setCalendars(newCalendars.filter(c => c.enabled).map(c => c.id));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<FlatList
|
||||||
|
data={calendars}
|
||||||
|
contentContainerStyle={styles.scrollViewInner}
|
||||||
|
renderItem={({ item, index }) => <CalendarItem color={item.color} title={item.title}
|
||||||
|
enabled={item.enabled} onSwitch={() => handleToggle(index)}/>}
|
||||||
|
ItemSeparatorComponent={() => <View style={styles.separator}/>}
|
||||||
|
// onRefresh={() => void(refreshCalendars)}
|
||||||
|
// refreshing={false}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
borderRadius: 16,
|
||||||
|
overflow: 'hidden',
|
||||||
|
backgroundColor: 'black'
|
||||||
|
},
|
||||||
|
scrollViewInner: {
|
||||||
|
padding: 8
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
height: 1,
|
||||||
|
marginVertical: 8,
|
||||||
|
backgroundColor: '#11161f'
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
borderRadius: 8,
|
||||||
|
overflow: 'hidden',
|
||||||
|
height: 52,
|
||||||
|
},
|
||||||
|
itemPressable: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingStart: 12,
|
||||||
|
paddingRight: 16,
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
itemDot: {
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
borderRadius: 9999,
|
||||||
|
marginRight: 20,
|
||||||
|
marginLeft: 8
|
||||||
|
},
|
||||||
|
itemTitle: {
|
||||||
|
fontSize: 16,
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
});
|
30
companion/src/screens/ModalScreen.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { Platform, StyleSheet, Text, View } from 'react-native';
|
||||||
|
|
||||||
|
// import EditScreenInfo from '../components/EditScreenInfo';
|
||||||
|
// import { Text, View } from '../components/Themed';
|
||||||
|
|
||||||
|
export default function ModalScreen() {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.title}>Modal</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
marginVertical: 30,
|
||||||
|
height: 1,
|
||||||
|
width: '80%',
|
||||||
|
},
|
||||||
|
});
|
31
companion/src/screens/ThemesScreen.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { StyleSheet, View, ScrollView, Text } from 'react-native';
|
||||||
|
|
||||||
|
export default function ThemesScreen() {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<ScrollView contentContainerStyle={styles.scrollViewInner}>
|
||||||
|
<Text style={styles.title}>Themes coming soon :)</Text>
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
borderRadius: 16,
|
||||||
|
overflow: 'hidden',
|
||||||
|
backgroundColor: 'black'
|
||||||
|
},
|
||||||
|
scrollViewInner: {
|
||||||
|
padding: 12,
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
color: '#567',
|
||||||
|
fontSize: 20,
|
||||||
|
marginTop: 32,
|
||||||
|
// fontWeight: 'bold',
|
||||||
|
}
|
||||||
|
});
|
4
companion/src/screens/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export { default as ModalScreen } from './ModalScreen';
|
||||||
|
export { default as CalendarsScreen } from './CalendarsScreen';
|
||||||
|
export { default as ThemesScreen } from './ThemesScreen';
|
||||||
|
export { default as AboutScreen } from './AboutScreen';
|
6
companion/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": "expo/tsconfig.base",
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true
|
||||||
|
}
|
||||||
|
}
|
36
companion/types.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Learn more about using TypeScript with React Navigation:
|
||||||
|
* https://reactnavigation.org/docs/typescript/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BottomTabScreenProps } from '@react-navigation/bottom-tabs';
|
||||||
|
import { CompositeScreenProps, NavigatorScreenParams } from '@react-navigation/native';
|
||||||
|
import { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
namespace ReactNavigation {
|
||||||
|
interface RootParamList extends RootStackParamList {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RootStackParamList = {
|
||||||
|
Root: NavigatorScreenParams<RootTabParamList> | undefined;
|
||||||
|
Modal: undefined;
|
||||||
|
NotFound: undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RootStackScreenProps<Screen extends keyof RootStackParamList> = NativeStackScreenProps<
|
||||||
|
RootStackParamList,
|
||||||
|
Screen
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type RootTabParamList = {
|
||||||
|
Calendars: undefined;
|
||||||
|
Theme: undefined;
|
||||||
|
About: undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RootTabScreenProps<Screen extends keyof RootTabParamList> = CompositeScreenProps<
|
||||||
|
BottomTabScreenProps<RootTabParamList, Screen>,
|
||||||
|
NativeStackScreenProps<RootStackParamList>
|
||||||
|
>;
|
24
watch/.project
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>Focus</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>json.validation.builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.tizen.web.project.builder.WebBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>json.validation.nature</nature>
|
||||||
|
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
|
||||||
|
<nature>org.tizen.web.project.builder.WebNature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
22
watch/.settings/.jsdtscope
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry excluding="**/bower_components/*|**/node_modules/*|**/*.min.js" kind="src" path="">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="provider" value="org.eclipse.wst.jsdt.web.core.internal.project.ModuleSourcePathProvider"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="hide" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
|
||||||
|
<classpathentry kind="con" path="org.tizen.web.project.initializer.WebLibraryInitializer"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipselabs.jsdt.jquery.core.CoflictLibrary_2.0"/>
|
||||||
|
<classpathentry kind="con" path="org.tizen.web.project.initializer.TizenLibraryInitializer"/>
|
||||||
|
<classpathentry kind="con" path="org.tizen.web.project.initializer.HTML5LibraryInitializer"/>
|
||||||
|
<classpathentry kind="con" path="org.tizen.web.project.initializer.W3CLibraryInitializer"/>
|
||||||
|
<classpathentry kind="con" path="org.tizen.web.project.initializer.ext.RESTSupport"/>
|
||||||
|
<classpathentry kind="output" path=""/>
|
||||||
|
</classpath>
|
2
watch/.settings/org.eclipse.wst.css.core.prefs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
css-profile/<project>=org.eclipse.wst.css.core.cssprofile.css3
|
||||||
|
eclipse.preferences.version=1
|
@ -0,0 +1 @@
|
|||||||
|
org.eclipse.wst.jsdt.launching.baseBrowserLibrary
|
1
watch/.settings/org.eclipse.wst.jsdt.ui.superType.name
Normal file
@ -0,0 +1 @@
|
|||||||
|
Window
|
24
watch/.sign/.manifest.tmp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
.vscode/tasks.json__DEL__z3Xirp4bBkVYOuLkeQRNUOUf0hU42upD11qVzov1carXdqFx4D+Fhxpcw1sSPddPpfAujQcSpGII
|
||||||
|
fNCXLTArdg==
|
||||||
|
LICENSE.Flora__DEL__DmG3CnrC92doof8/csn15OLKg6OO1WLYfQ9uEQQ/n/VN17C/uS3lvfaTnU0OZ+jz8fLIRG9Zqo75
|
||||||
|
upc4DKODmg==
|
||||||
|
NOTICE__DEL__fT2i6Z4ODh+DqtCjQyeK5uN4x+niPkI2udRx96CtU2imYHAMYXHhKFZjjQp3fmpZ6frYv4E3mbBz
|
||||||
|
6d2rvCgAxA==
|
||||||
|
config.xml__DEL__wEoe6rrKdUhpgIG6jEzvr4aXCSyObOwI03rTf6Oh+MRp4JCMN5B7fbMxFT4xdOlNxiU7vzRmdaHa
|
||||||
|
Uo/N0yXsQw==
|
||||||
|
css/style.css__DEL__pGxu4RkFZ31ePUZ9DZfg8oC27yh0KG+WeQvtJ3OqL94w3NK4B2KCXgxcwV3u0K8l3iLjL0mPqawJ
|
||||||
|
55eCkvo4Pw==
|
||||||
|
icon.png__DEL__V19vQSoQmeA+0SAe2zZ5n7ZfhLXwzW1huc0SL0x93gfRFoZJZJYHHE389ozH0pyeuXZsJ8C9miyp
|
||||||
|
Mq7Z/83Oag==
|
||||||
|
index.html__DEL__Qv+ECIfSc7x0qgabjOTQ6x0THpMMgdmNFAltsBFUwpLyPwOERbOw7W/rFuJdbr+39Aa8lBZezQX4
|
||||||
|
/J2XvzwzgQ==
|
||||||
|
js/app.js__DEL__LW2jSMYKpzVwyQl6Ss6Ci9XaGattqwHZuKr1dYDZmZF0fg0GDB7PcDcDA2MF7Y8EGRD+UIBUp0ug
|
||||||
|
DwCMp6BWaQ==
|
||||||
|
res/glow.png__DEL__+raW+o3PLDMsOdZ46jMqZn56GunBtPfuBWnUSYSEsP6msyDfVULxjD/gOzx0sdpKbW3+IV1pFPfs
|
||||||
|
jelRSZ+DPQ==
|
||||||
|
res/icon.svg__DEL__PfH43OvgmRvOyHlhzqpEmScP2C13iRlgQdCRliuCLygGJPS3Kt5J9/ocavcqiOZw4lBFfHKm0N4A
|
||||||
|
QFaT2CDakg==
|
||||||
|
version.txt__DEL__jJtvGOhH+xRt5nb2nvtEt+HGe/PG0CnhcMjO6XKTjHZEjeAZ5yuYeNEv9jXlGeLwx2zOOKSyvcob
|
||||||
|
EviMmoUiyw==
|
||||||
|
author-signature.xml__DEL__aIlAcoz0mBFIGxlZX9W9GwzktPq3vT1cX84vEFNZ2Rwe3CQ8l5nQxXEFCZd8k+eUwnLWgE4X8CU0
|
||||||
|
emsvomRuZg==
|
119
watch/.sign/author-signature.xml
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#" Id="AuthorSignature">
|
||||||
|
<SignedInfo>
|
||||||
|
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
|
||||||
|
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"></SignatureMethod>
|
||||||
|
<Reference URI=".vscode%2Ftasks.json">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>z3Xirp4bBkVYOuLkeQRNUOUf0hU42upD11qVzov1carXdqFx4D+Fhxpcw1sSPddPpfAujQcSpGII
|
||||||
|
fNCXLTArdg==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="LICENSE.Flora">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>DmG3CnrC92doof8/csn15OLKg6OO1WLYfQ9uEQQ/n/VN17C/uS3lvfaTnU0OZ+jz8fLIRG9Zqo75
|
||||||
|
upc4DKODmg==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="NOTICE">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>fT2i6Z4ODh+DqtCjQyeK5uN4x+niPkI2udRx96CtU2imYHAMYXHhKFZjjQp3fmpZ6frYv4E3mbBz
|
||||||
|
6d2rvCgAxA==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="config.xml">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>wEoe6rrKdUhpgIG6jEzvr4aXCSyObOwI03rTf6Oh+MRp4JCMN5B7fbMxFT4xdOlNxiU7vzRmdaHa
|
||||||
|
Uo/N0yXsQw==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="css%2Fstyle.css">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>pGxu4RkFZ31ePUZ9DZfg8oC27yh0KG+WeQvtJ3OqL94w3NK4B2KCXgxcwV3u0K8l3iLjL0mPqawJ
|
||||||
|
55eCkvo4Pw==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="icon.png">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>V19vQSoQmeA+0SAe2zZ5n7ZfhLXwzW1huc0SL0x93gfRFoZJZJYHHE389ozH0pyeuXZsJ8C9miyp
|
||||||
|
Mq7Z/83Oag==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="index.html">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>Qv+ECIfSc7x0qgabjOTQ6x0THpMMgdmNFAltsBFUwpLyPwOERbOw7W/rFuJdbr+39Aa8lBZezQX4
|
||||||
|
/J2XvzwzgQ==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="js%2Fapp.js">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>LW2jSMYKpzVwyQl6Ss6Ci9XaGattqwHZuKr1dYDZmZF0fg0GDB7PcDcDA2MF7Y8EGRD+UIBUp0ug
|
||||||
|
DwCMp6BWaQ==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="res%2Fglow.png">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>+raW+o3PLDMsOdZ46jMqZn56GunBtPfuBWnUSYSEsP6msyDfVULxjD/gOzx0sdpKbW3+IV1pFPfs
|
||||||
|
jelRSZ+DPQ==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="res%2Ficon.svg">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>PfH43OvgmRvOyHlhzqpEmScP2C13iRlgQdCRliuCLygGJPS3Kt5J9/ocavcqiOZw4lBFfHKm0N4A
|
||||||
|
QFaT2CDakg==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="version.txt">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>jJtvGOhH+xRt5nb2nvtEt+HGe/PG0CnhcMjO6XKTjHZEjeAZ5yuYeNEv9jXlGeLwx2zOOKSyvcob
|
||||||
|
EviMmoUiyw==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="#prop">
|
||||||
|
<Transforms>
|
||||||
|
<Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11"></Transform>
|
||||||
|
</Transforms>
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>aXbSAVgmAz0GsBUeZ1UmNDRrxkWhDUVGb45dZcNRq429wX3X+x6kaXT3NdNDTSNVTU+ypkysPMGv
|
||||||
|
QY10fG1EWQ==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
</SignedInfo>
|
||||||
|
<SignatureValue>
|
||||||
|
aaef+oiqV9BcDcwmR65XSRr3diPD5fjpp3Cd4QGdN/7tP4xtF34fiwXI2zQb9Tw2V3d5w2JWyoU9
|
||||||
|
yXn3iknub56ohKuhDWPLQEVua/g9IVB1V3vWMg0a6Y7aVKw1Bm3ejM1WBAutI/iYDB4Pv90VKN5+
|
||||||
|
Pkd9hOc8jAZBRNIeqBpBsJHPqyIJHJSxIcfZNClE/0Xna0WKg9H0BPYeqwbyHCGL6iw+RI9wBRK9
|
||||||
|
wLsM1lg/U0gZfAfpNbHtztcMMLmLriLRtTPAAHzNPM2QqP9Q7gD5UjlXle9d1d9ihJ8K3VwlISso
|
||||||
|
w8rfJ35NtPbsQ5fOJv64ysknx4+KLPKZmYx4JQ==
|
||||||
|
</SignatureValue>
|
||||||
|
<KeyInfo>
|
||||||
|
<X509Data>
|
||||||
|
<X509Certificate>
|
||||||
|
MIIDwjCCAqqgAwIBAgIBATANBgkqhkiG9w0BAQ0FADCBsDELMAkGA1UEBhMCS1IxFDASBgNVBAgM
|
||||||
|
C1NvdXRoIEtvcmVhMQ4wDAYDVQQHDAVTdXdvbjEmMCQGA1UECgwdU2Ftc3VuZyBFbGVjdHJvbmlj
|
||||||
|
cyBDby4sIEx0ZC4xDzANBgNVBAsMBk1vYmlsZTEgMB4GA1UEAwwXU2Ftc3VuZyBBdXRob3IgQ0Eg
|
||||||
|
Q2xhc3MxIDAeBgkqhkiG9w0BCQEWEXRpemVuQHNhbXN1bmcuY29tMB4XDTIxMDkwOTIzMjMxMloX
|
||||||
|
DTIyMDkwOTIzMjMxMlowfDELMAkGA1UEBhMCQ0ExGTAXBgNVBAgTEEJyaXRpc2ggQ29sdW1iaWEx
|
||||||
|
FTATBgNVBAcTDFBvd2VsbCBSaXZlcjEYMBYGA1UEChMPQXVyYWlsdXMgRGVzaWduMQkwBwYDVQQL
|
||||||
|
EwAxFjAUBgNVBAMTDUF1cmkgQ29sbGluZ3MwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||||
|
AQCn8wFEN7HYZaOQBHUfQuFV1dEwpjYz/FrFJOIwXbVoV1e+s87ya6h6fOa/F2kyyRVbggcQybCk
|
||||||
|
IzPqFRIWUEFXq6WWA6sCEFYngcCKtbvR109jYXgTiq6KdslxL+aCBp7+cm4A42TI2FZ0554DDUCx
|
||||||
|
MTHbRS4+hTJfiLq/AN+zcllZFom7qJ35Oz+JxXvShnqjGcKYGy8s6H8hhWtmh8ajJIKcg2fBCXs2
|
||||||
|
yC0Qqn1OWhfD5nG23KzVAj4zJy4iB1hbQAvrx4jRE+bvNAiN8SjSxxqXqk6NTU6RTIir3auHdcDp
|
||||||
|
ZrrKBBvAxN22K/3T6sefpW0mT+nOPCguFoLBRx5tAgMBAAGjGjAYMAkGA1UdEwQCMAAwCwYDVR0P
|
||||||
|
BAQDAgeAMA0GCSqGSIb3DQEBDQUAA4IBAQCdmUZNTNEpTrwqpMlTNtbt4R/SHE/N+ia8ACkUtM4F
|
||||||
|
IxByp86ZOu8LuQkNQFDu3dG8R1btyraoUkU3haQK7m74PSKO6na7NTS+sYHqaAnxj6LsTwmGMSc2
|
||||||
|
RZMnS5fM2/vdoIgM99M2dq1xUrQhzaS7DRas3awr7+6sRHAkRAgMl/89yEgZ7TKx2yHHM0alMXRR
|
||||||
|
otyUa2JiHdYDaTFECmqQQkSzn2+Ba2Blv5V7baHYLNxvnp5FaqZRKP06SjF5IAF5KGvsygvLpFxi
|
||||||
|
tzTQsc+v4SvbILQju4tVkIDnSLZD107l6HQnb+Y9NJXfPWkcPHexskoCQ28wfZBN/gDIXRhy
|
||||||
|
</X509Certificate>
|
||||||
|
<X509Certificate>
|
||||||
|
MIIDmzCCAoOgAwIBAgICYygwDQYJKoZIhvcNAQELBQAwXjEaMBgGA1UECgwRVGl6ZW4gQXNzb2Np
|
||||||
|
YXRpb24xGjAYBgNVBAsMEVRpemVuIEFzc29jaWF0aW9uMSQwIgYDVQQDDBtUaXplbiBEZXZlbG9w
|
||||||
|
ZXJzIFJvb3QgQ2xhc3MwHhcNMTMxMjMwMTUwNTU4WhcNMjgxMjI2MTUwNTU4WjCBsDELMAkGA1UE
|
||||||
|
BhMCS1IxFDASBgNVBAgMC1NvdXRoIEtvcmVhMQ4wDAYDVQQHDAVTdXdvbjEmMCQGA1UECgwdU2Ft
|
||||||
|
c3VuZyBFbGVjdHJvbmljcyBDby4sIEx0ZC4xDzANBgNVBAsMBk1vYmlsZTEgMB4GA1UEAwwXU2Ft
|
||||||
|
c3VuZyBBdXRob3IgQ0EgQ2xhc3MxIDAeBgkqhkiG9w0BCQEWEXRpemVuQHNhbXN1bmcuY29tMIIB
|
||||||
|
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs+tRBgnKJs8j7BFg8/UftqbqTCYBV3Jrg1vK
|
||||||
|
YvEuoTfntYz2uT2SO67raiCsZBAYvJnP54ExkdV++UgB7BDGniWz7bA1pYKak5kNK5jtLQt2DmZX
|
||||||
|
3qgaLjMyoAz+293CxrBQO4h8NaTQGsO/WLpeQq2Y1ZEnHsq+EUn90H6Vm0HNW+KUayGPYdey+QSW
|
||||||
|
iiv+L++TSuHrw0b16GYn83emiTnKTCmwpSOZ712Gy9kccl46/K4C8skEDVZjTk9s7r/MN9ZNZsqR
|
||||||
|
brT/3AYcrF4ao8ipwlHK91WJBXXaiQICvp/dNfCSDWpTWy7z4XmgP16pSLnfgZlwEwWfiaavHRNM
|
||||||
|
mwIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB/ZlNMTzlIHqC3mFSq
|
||||||
|
ptuQDZG96XnYqiWsbYkqGgNhcq6c/B3TQsg7Z8cxXY/eqJQDN5gbrIpiUugMRdSOpAEcxF3lwd5k
|
||||||
|
oOzVLn+3I7x1k6Q4pZdi1fJx+1XjCtrQgPqtvwM77urNqIA1MSG6HUPxYAKkRKjWPsg346E8S/c1
|
||||||
|
Hq4UVBYEFcDC467uvWtYjxjEVQTmNUaUcQLU9P6VEL4QW+t7V54IN6IJDr9HoOGSgApxIBDDU46b
|
||||||
|
MUwl+yK0GPvhrviwfVPkfmys1hn5N+gWectQVpBB1gbfy2KlLCCvW/Kl1VmtYz1kWwTyG8bwcjE0
|
||||||
|
GLkwKNN5lPod+FmMhuW9
|
||||||
|
</X509Certificate>
|
||||||
|
</X509Data>
|
||||||
|
</KeyInfo>
|
||||||
|
<Object Id="prop"><SignatureProperties xmlns:dsp="http://www.w3.org/2009/xmldsig-properties"><SignatureProperty Id="profile" Target="#AuthorSignature"><dsp:Profile URI="http://www.w3.org/ns/widgets-digsig#profile"></dsp:Profile></SignatureProperty><SignatureProperty Id="role" Target="#AuthorSignature"><dsp:Role URI="http://www.w3.org/ns/widgets-digsig#role-author"></dsp:Role></SignatureProperty><SignatureProperty Id="identifier" Target="#AuthorSignature"><dsp:Identifier></dsp:Identifier></SignatureProperty></SignatureProperties></Object>
|
||||||
|
</Signature>
|
125
watch/.sign/signature1.xml
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#" Id="DistributorSignature">
|
||||||
|
<SignedInfo>
|
||||||
|
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
|
||||||
|
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"></SignatureMethod>
|
||||||
|
<Reference URI=".vscode%2Ftasks.json">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>z3Xirp4bBkVYOuLkeQRNUOUf0hU42upD11qVzov1carXdqFx4D+Fhxpcw1sSPddPpfAujQcSpGII
|
||||||
|
fNCXLTArdg==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="LICENSE.Flora">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>DmG3CnrC92doof8/csn15OLKg6OO1WLYfQ9uEQQ/n/VN17C/uS3lvfaTnU0OZ+jz8fLIRG9Zqo75
|
||||||
|
upc4DKODmg==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="NOTICE">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>fT2i6Z4ODh+DqtCjQyeK5uN4x+niPkI2udRx96CtU2imYHAMYXHhKFZjjQp3fmpZ6frYv4E3mbBz
|
||||||
|
6d2rvCgAxA==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="author-signature.xml">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>aIlAcoz0mBFIGxlZX9W9GwzktPq3vT1cX84vEFNZ2Rwe3CQ8l5nQxXEFCZd8k+eUwnLWgE4X8CU0
|
||||||
|
emsvomRuZg==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="config.xml">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>wEoe6rrKdUhpgIG6jEzvr4aXCSyObOwI03rTf6Oh+MRp4JCMN5B7fbMxFT4xdOlNxiU7vzRmdaHa
|
||||||
|
Uo/N0yXsQw==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="css%2Fstyle.css">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>pGxu4RkFZ31ePUZ9DZfg8oC27yh0KG+WeQvtJ3OqL94w3NK4B2KCXgxcwV3u0K8l3iLjL0mPqawJ
|
||||||
|
55eCkvo4Pw==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="icon.png">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>V19vQSoQmeA+0SAe2zZ5n7ZfhLXwzW1huc0SL0x93gfRFoZJZJYHHE389ozH0pyeuXZsJ8C9miyp
|
||||||
|
Mq7Z/83Oag==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="index.html">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>Qv+ECIfSc7x0qgabjOTQ6x0THpMMgdmNFAltsBFUwpLyPwOERbOw7W/rFuJdbr+39Aa8lBZezQX4
|
||||||
|
/J2XvzwzgQ==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="js%2Fapp.js">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>LW2jSMYKpzVwyQl6Ss6Ci9XaGattqwHZuKr1dYDZmZF0fg0GDB7PcDcDA2MF7Y8EGRD+UIBUp0ug
|
||||||
|
DwCMp6BWaQ==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="res%2Fglow.png">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>+raW+o3PLDMsOdZ46jMqZn56GunBtPfuBWnUSYSEsP6msyDfVULxjD/gOzx0sdpKbW3+IV1pFPfs
|
||||||
|
jelRSZ+DPQ==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="res%2Ficon.svg">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>PfH43OvgmRvOyHlhzqpEmScP2C13iRlgQdCRliuCLygGJPS3Kt5J9/ocavcqiOZw4lBFfHKm0N4A
|
||||||
|
QFaT2CDakg==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="version.txt">
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>jJtvGOhH+xRt5nb2nvtEt+HGe/PG0CnhcMjO6XKTjHZEjeAZ5yuYeNEv9jXlGeLwx2zOOKSyvcob
|
||||||
|
EviMmoUiyw==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
<Reference URI="#prop">
|
||||||
|
<Transforms>
|
||||||
|
<Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11"></Transform>
|
||||||
|
</Transforms>
|
||||||
|
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
|
||||||
|
<DigestValue>/r5npk2VVA46QFJnejgONBEh4BWtjrtu9x/IFeLksjWyGmB/cMWKSJWQl7aU3YRQRZ3AesG8gF7q
|
||||||
|
GyvKX9Snig==</DigestValue>
|
||||||
|
</Reference>
|
||||||
|
</SignedInfo>
|
||||||
|
<SignatureValue>
|
||||||
|
b5PqbrJOZSnLXSUnNj/0NTTNFwjblrGQuH4RUF8ZGkyLuktkEvJtVX/1HTQhcNjv4iJ7Eb82casB
|
||||||
|
JdeKq3Ml3qIL5UlRJZRcltdcPmU0Fl1aXlrDztBTotaS22BD012HEwRuEI8hBCwlOlNnPB2Eq6IC
|
||||||
|
ZLhSRPAjK8nrbnnyhkOqvaFNY/VU6jhchZ8fNvD+6LU/GhNqeleWD8Db65YvFR/qpAQnTPSKcDXv
|
||||||
|
sAnBBfzhwqp8N1teMYNpAnReWWZ/b/N+KeTUhjCrJjMKFXdNtFPxPlB/jTEdlJv6tnGFU1Uv0hTD
|
||||||
|
80CslwCx9ZA768tpYyTrsf53eFuAK/4FXRo37g==
|
||||||
|
</SignatureValue>
|
||||||
|
<KeyInfo>
|
||||||
|
<X509Data>
|
||||||
|
<X509Certificate>
|
||||||
|
MIID2DCCAsCgAwIBAgIBZTANBgkqhkiG9w0BAQ0FADCBnjELMAkGA1UEBhMCS1IxFDASBgNVBAgM
|
||||||
|
C1NvdXRoIEtvcmVhMQ4wDAYDVQQHDAVTdXdvbjEmMCQGA1UECgwdU2Ftc3VuZyBFbGVjdHJvbmlj
|
||||||
|
cyBDby4sIEx0ZC4xDzANBgNVBAsMBk1vYmlsZTEwMC4GA1UEAwwnU2Ftc3VuZyBUaXplbiBERVZF
|
||||||
|
TE9QRVIgUHVibGljIENBIENsYXNzMB4XDTIxMDkwOTIzMjMzN1oXDTIyMDkwOTIzMjMzN1owZjER
|
||||||
|
MA8GA1UEAwwIVGl6ZW5TREsxCTAHBgNVBAsMADEJMAcGA1UECgwAMQkwBwYDVQQHDAAxCTAHBgNV
|
||||||
|
BAgMADEJMAcGA1UEBhMAMRowGAYJKoZIhvcNAQkBFgttZUBhdXJpLnh5ejCCASIwDQYJKoZIhvcN
|
||||||
|
AQEBBQADggEPADCCAQoCggEBAIdyIjaEkrlgDNOBl0AD/sAg1MEVkIgTQYzWk/F1Cb/nB4ALQ0p+
|
||||||
|
JsYpuHoDySy1ioFqjz5r3dULmnOF1vH1xrk4ypTznYgJtccFknY3Ii6dvss/LCW3NHt7KY6j6UR7
|
||||||
|
xFbqiM/7sryK3qlXZEIJ5QW06pD+pYS3aMpREmBuLhuFqsOKcasST+osu+ES3sFGqN9lv714Kema
|
||||||
|
eneBvuw9FaHEeCx0xWpA/wtWoBSSjckxft03+zzgiOCLN+q39OGJIZDBe9IKY63CsTvbBEIRDxhZ
|
||||||
|
5B7hIbm7XaerRl/FGNPtvo2mA0oFAMYu4stEG/Kygk2J82y/VFmOo9qFStzk280CAwEAAaNYMFYw
|
||||||
|
VAYDVR0RBE0wS4YUVVJOOnRpemVuOnBhY2thZ2VpZD2GM1VSTjp0aXplbjpkZXZpY2VpZD0yLjAj
|
||||||
|
TFRaaDFEUkNocWxGdlp6RUhPd0V2TWtmT29jPTANBgkqhkiG9w0BAQ0FAAOCAQEAS+VYNYT/r/tn
|
||||||
|
l0zMGjoy3lREd3Zdbt8V488IzXT3WC84VAXcr6bl7k/XIzW4XSsB6Yqv7eB36DoPNaCT43pFCvP6
|
||||||
|
ReSYjYeQ/jnmcymQ5D8r+8VHNVI3xeSZzDaJovpBBVM3nmaB4UbeFSEvddmzIuVJX6ZTDIsKDt1l
|
||||||
|
JhM+a8GekvrkZhhbk+4XDy4rFK3t7IF0+CKAEQlxPbOE/iyqgLGAOdQR23HNpEMWK5MAV2lBUkjw
|
||||||
|
QzfbLiXNJ4mv4aeAyqD361Rp2pilNGPMv7l7N9Z05X4mJ5bDsurDKz7GjqYEEYQpOW8pTJdNURuK
|
||||||
|
Z7Fqo6RFiNUNP0CzKraVnXa6Tw==
|
||||||
|
</X509Certificate>
|
||||||
|
<X509Certificate>
|
||||||
|
MIIDuzCCAqOgAwIBAgICL+cwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAktSMRQwEgYDVQQI
|
||||||
|
DAtTb3V0aCBLb3JlYTEOMAwGA1UEBwwFU3V3b24xJjAkBgNVBAoMHVNhbXN1bmcgRWxlY3Ryb25p
|
||||||
|
Y3MgQ28uLCBMdGQuMTIwMAYDVQQDDClTYW1zdW5nIFRpemVuIERFVkVMT1BFUiBQdWJsaWMgUm9v
|
||||||
|
dCBDbGFzczAeFw0xMzEyMzAxNTAxNDdaFw0yODEyMjYxNTAxNDdaMIGeMQswCQYDVQQGEwJLUjEU
|
||||||
|
MBIGA1UECAwLU291dGggS29yZWExDjAMBgNVBAcMBVN1d29uMSYwJAYDVQQKDB1TYW1zdW5nIEVs
|
||||||
|
ZWN0cm9uaWNzIENvLiwgTHRkLjEPMA0GA1UECwwGTW9iaWxlMTAwLgYDVQQDDCdTYW1zdW5nIFRp
|
||||||
|
emVuIERFVkVMT1BFUiBQdWJsaWMgQ0EgQ2xhc3MwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
||||||
|
AoIBAQCkqW9d0zO5NFOc7u164DKe9Yx+yEgUnsbhnJasqHaqT71qaMxyCOjyysZi7gGycDcgmLcU
|
||||||
|
tr2wSMTGWPibK8SrJ8bV/J1cy9nTpljM3s+lbPIVVxZeufhJkU79tXImHLolERd0vui+rj0Xpd9O
|
||||||
|
zlyNTRt0+PWVT1taWcbfHL7pUD25hMkTc8C3bC+dUoT1/RhCkXgmUvIor3EnnU0rBtAl4qNtg5y1
|
||||||
|
7RLT8dyicieAHCcW923YC/ngMZxnBjbL2Ht1RBUvrL6K3X5+l0VeF4qC3g7TKm/a/SGjq12ZD4sc
|
||||||
|
7rglzjSNlTVSGag6/2WkqxE6HheEBWaloYOdauzwydFNAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8w
|
||||||
|
DQYJKoZIhvcNAQELBQADggEBAG88wKjKeTbj5I6wMEvRihd+gWrESJX3bXUSlHWjUfYKcvg3GzM9
|
||||||
|
cdooscqJdYgN8ezQuYiJQRk7BaPHSlPGkchsPUjvSM6WvIn9IT+VsEuVBrZNbZMSv6lM6wNig3TE
|
||||||
|
h2OxFTW6Bt8mCZB74EP2wb+39Fr5aJwWvmkxQjct3/O+GYPQhe0Lu1qpbbMhzafYYybSmO+om02V
|
||||||
|
cTWyJ/vtLUNyGfN7aHyBm6PFE5piAfpBaO9Az+zWil1HOsnzu1tGGnyV6IzQ/UiIo5P2sD8I5lEO
|
||||||
|
Xdf9Eicy9y5z2Ci2zrmDcAGWL8o5wOWrCqML2ijximFzyQ/swlMC9xBIX9RCFws=
|
||||||
|
</X509Certificate>
|
||||||
|
</X509Data>
|
||||||
|
</KeyInfo>
|
||||||
|
<Object Id="prop"><SignatureProperties xmlns:dsp="http://www.w3.org/2009/xmldsig-properties"><SignatureProperty Id="profile" Target="#DistributorSignature"><dsp:Profile URI="http://www.w3.org/ns/widgets-digsig#profile"></dsp:Profile></SignatureProperty><SignatureProperty Id="role" Target="#DistributorSignature"><dsp:Role URI="http://www.w3.org/ns/widgets-digsig#role-distributor"></dsp:Role></SignatureProperty><SignatureProperty Id="identifier" Target="#DistributorSignature"><dsp:Identifier></dsp:Identifier></SignatureProperty></SignatureProperties></Object>
|
||||||
|
</Signature>
|
12
watch/.tproject
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<tproject xmlns="http://www.tizen.org/tproject">
|
||||||
|
<platforms>
|
||||||
|
<platform>
|
||||||
|
<name>wearable-4.0</name>
|
||||||
|
</platform>
|
||||||
|
</platforms>
|
||||||
|
<package>
|
||||||
|
<blacklist/>
|
||||||
|
<resFallback autoGen="true"/>
|
||||||
|
</package>
|
||||||
|
</tproject>
|
BIN
watch/Focus.wgt
Normal file
206
watch/LICENSE.Flora
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
Flora License
|
||||||
|
|
||||||
|
Version 1.1, April, 2013
|
||||||
|
|
||||||
|
http://floralicense.org/license/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and
|
||||||
|
all other entities that control, are controlled by, or are
|
||||||
|
under common control with that entity. For the purposes of
|
||||||
|
this definition, "control" means (i) the power, direct or indirect,
|
||||||
|
to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (ii) ownership of fifty percent (50%)
|
||||||
|
or more of the outstanding shares, or (iii) beneficial ownership of
|
||||||
|
such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation source,
|
||||||
|
and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or Object form,
|
||||||
|
made available under the License, as indicated by a copyright notice
|
||||||
|
that is included in or attached to the work (an example is provided
|
||||||
|
in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object form,
|
||||||
|
that is based on (or derived from) the Work and for which the editorial
|
||||||
|
revisions, annotations, elaborations, or other modifications represent,
|
||||||
|
as a whole, an original work of authorship. For the purposes of this License,
|
||||||
|
Derivative Works shall not include works that remain separable from,
|
||||||
|
or merely link (or bind by name) to the interfaces of, the Work and
|
||||||
|
Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including the original
|
||||||
|
version of the Work and any modifications or additions to that Work or
|
||||||
|
Derivative Works thereof, that is intentionally submitted to Licensor
|
||||||
|
for inclusion in the Work by the copyright owner or by an individual or
|
||||||
|
Legal Entity authorized to submit on behalf of the copyright owner.
|
||||||
|
For the purposes of this definition, "submitted" means any form of
|
||||||
|
electronic, verbal, or written communication sent to the Licensor or
|
||||||
|
its representatives, including but not limited to communication on
|
||||||
|
electronic mailing lists, source code control systems, and issue
|
||||||
|
tracking systems that are managed by, or on behalf of, the Licensor
|
||||||
|
for the purpose of discussing and improving the Work, but excluding
|
||||||
|
communication that is conspicuously marked or otherwise designated
|
||||||
|
in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
"Tizen Certified Platform" shall mean a software platform that complies
|
||||||
|
with the standards set forth in the Tizen Compliance Specification
|
||||||
|
and passes the Tizen Compliance Tests as defined from time to time
|
||||||
|
by the Tizen Technical Steering Group and certified by the Tizen
|
||||||
|
Association or its designated agent.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work
|
||||||
|
solely as incorporated into a Tizen Certified Platform, where such
|
||||||
|
license applies only to those patent claims licensable by such
|
||||||
|
Contributor that are necessarily infringed by their Contribution(s)
|
||||||
|
alone or by combination of their Contribution(s) with the Work solely
|
||||||
|
as incorporated into a Tizen Certified Platform to which such
|
||||||
|
Contribution(s) was submitted. If You institute patent litigation
|
||||||
|
against any entity (including a cross-claim or counterclaim
|
||||||
|
in a lawsuit) alleging that the Work or a Contribution incorporated
|
||||||
|
within the Work constitutes direct or contributory patent infringement,
|
||||||
|
then any patent licenses granted to You under this License for that
|
||||||
|
Work shall terminate as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof pursuant to the copyright license
|
||||||
|
above, in any medium, with or without modifications, and in Source or
|
||||||
|
Object form, provided that You meet the following conditions:
|
||||||
|
|
||||||
|
1. You must give any other recipients of the Work or Derivative Works
|
||||||
|
a copy of this License; and
|
||||||
|
2. You must cause any modified files to carry prominent notices stating
|
||||||
|
that You changed the files; and
|
||||||
|
3. You must retain, in the Source form of any Derivative Works that
|
||||||
|
You distribute, all copyright, patent, trademark, and attribution
|
||||||
|
notices from the Source form of the Work, excluding those notices
|
||||||
|
that do not pertain to any part of the Derivative Works; and
|
||||||
|
4. If the Work includes a "NOTICE" text file as part of its distribution,
|
||||||
|
then any Derivative Works that You distribute must include a readable
|
||||||
|
copy of the attribution notices contained within such NOTICE file,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works, in at least one of the following places:
|
||||||
|
within a NOTICE text file distributed as part of the Derivative Works;
|
||||||
|
within the Source form or documentation, if provided along with the
|
||||||
|
Derivative Works; or, within a display generated by the Derivative Works,
|
||||||
|
if and wherever such third-party notices normally appear.
|
||||||
|
The contents of the NOTICE file are for informational purposes only
|
||||||
|
and do not modify the License. You may add Your own attribution notices
|
||||||
|
within Derivative Works that You distribute, alongside or as an addendum
|
||||||
|
to the NOTICE text from the Work, provided that such additional attribution
|
||||||
|
notices cannot be construed as modifying the License. You may add Your own
|
||||||
|
copyright statement to Your modifications and may provide additional or
|
||||||
|
different license terms and conditions for use, reproduction, or
|
||||||
|
distribution of Your modifications, or for any such Derivative Works
|
||||||
|
as a whole, provided Your use, reproduction, and distribution of
|
||||||
|
the Work otherwise complies with the conditions stated in this License
|
||||||
|
and your own copyright statement or terms and conditions do not conflict
|
||||||
|
the conditions stated in the License including section 3.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Flora License to your work
|
||||||
|
|
||||||
|
To apply the Flora License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2015 Samsung Electronics Co., Ltd.
|
||||||
|
|
||||||
|
Licensed under the Flora License, Version 1.1 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://floralicense.org/license/
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
3
watch/NOTICE
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved.
|
||||||
|
Except as noted, this software is licensed under Flora License, Version 1.
|
||||||
|
Please, see the LICENSE.Flora file for Flora License, Version 1 terms and conditions.
|
15
watch/config.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<widget xmlns:tizen='http://tizen.org/ns/widgets' xmlns='http://www.w3.org/ns/widgets' id='http://yourdomain/Focus' version='1.0.0' viewmodes='maximized'>
|
||||||
|
<tizen:application id='kFVD8WK8ro.Focus' package='kFVD8WK8ro' required_version='2.3.1' ambient_support='enable'/>
|
||||||
|
<tizen:category name='http://tizen.org/category/wearable_clock'/>
|
||||||
|
<feature name='http://tizen.org/feature/screen.shape.circle'/>
|
||||||
|
<feature name='http://tizen.org/feature/screen.size.all'/>
|
||||||
|
<feature name='http://tizen.org/feature/calendar'/>
|
||||||
|
<feature name='http://tizen.org/feature/calendar.read'/>
|
||||||
|
<feature name='http://tizen.org/feature/calendar.write'/>
|
||||||
|
<content src='index.html'/>
|
||||||
|
<icon src='icon.png'/>
|
||||||
|
<name>Focus</name>
|
||||||
|
<tizen:privilege name='http://tizen.org/privilege/alarm'/>
|
||||||
|
<tizen:profile name='wearable'/>
|
||||||
|
</widget>
|
24
watch/css/style.css
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
color: #fff;
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
display: -webkit-flex;
|
||||||
|
-webkit-align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
BIN
watch/icon.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
17
watch/index.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
||||||
|
<title>AmbientWatch</title>
|
||||||
|
<link rel="stylesheet" href="css/style.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
</div>
|
||||||
|
<script src="js/app.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
313
watch/js/app.js
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
let theme = {
|
||||||
|
/** 0: none, 1: quarter, 2: hours, 3: hours + minute */
|
||||||
|
notches: 3,
|
||||||
|
/** False: don't display, True: display */
|
||||||
|
events: true,
|
||||||
|
/** False: don't show minute hand, True: show minute hand */
|
||||||
|
minutes: true,
|
||||||
|
/** False: don't show center glow, True: show center glow */
|
||||||
|
glow: true,
|
||||||
|
/** Center glow color. */
|
||||||
|
glow_a: '#4294ff',
|
||||||
|
/** Moving glow 1 color. */
|
||||||
|
glow_b: 'rgba(5, 124, 242, 0.6)',
|
||||||
|
/** Moving glow 2 color. */
|
||||||
|
glow_c: 'rgba(198, 0, 237, 1)',
|
||||||
|
// /** Center glow color. */
|
||||||
|
// glow_a: '#e838ff',
|
||||||
|
// /** Moving glow 1 color. */
|
||||||
|
// glow_b: 'rgba(255, 38, 56, 0.6)',
|
||||||
|
// /** Moving glow 2 color. */
|
||||||
|
// glow_c: 'rgba(179, 38, 255, 1)',
|
||||||
|
}
|
||||||
|
|
||||||
|
let image_loaded = false;
|
||||||
|
let window_loaded = false;
|
||||||
|
|
||||||
|
window.onload = () => {
|
||||||
|
window_loaded = true;
|
||||||
|
if (image_loaded) init();
|
||||||
|
};
|
||||||
|
|
||||||
|
const glow_image = document.createElement('img');
|
||||||
|
glow_image.src = 'res/glow.png';
|
||||||
|
glow_image.onload = () => {
|
||||||
|
image_loaded = true;
|
||||||
|
if (window_loaded) init();
|
||||||
|
};
|
||||||
|
|
||||||
|
const glow_canvas = document.createElement('canvas');
|
||||||
|
glow_canvas.width = 128;
|
||||||
|
glow_canvas.height = 128;
|
||||||
|
const glow_ctx = glow_canvas.getContext('2d');
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
const canvas = document.querySelector('#canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
canvas.width = document.body.clientWidth;
|
||||||
|
canvas.height = canvas.width;
|
||||||
|
const clockRadius = document.body.clientWidth / 2;
|
||||||
|
|
||||||
|
let rot = 0;
|
||||||
|
let ambient = false;
|
||||||
|
|
||||||
|
const EVENT_WIDTH = 22;
|
||||||
|
let EVENT_BUFFER = theme.notches === 0 ? 2 : 14;
|
||||||
|
const TITLE_BUFFER = 2;
|
||||||
|
const TITLE_SIZE = 16;
|
||||||
|
const NOTCH_BUFFER = 2;
|
||||||
|
const DATE_SIZE = 20;
|
||||||
|
|
||||||
|
const COLOR_LIGHT_DIM = 'rgba(65, 199, 232, 0.15)';
|
||||||
|
const COLOR_LIGHT_OVERLAY = 'rgba(65, 199, 232, 0.33)';
|
||||||
|
const COLOR_MINUTE_HAND = 'rgba(65, 199, 232, 0.8)';
|
||||||
|
|
||||||
|
const degToRad = (deg) => deg * (Math.PI / 180);
|
||||||
|
|
||||||
|
const createDate = (hour, minute) => {
|
||||||
|
const date = new Date();
|
||||||
|
date.setHours(hour, minute, 0);
|
||||||
|
return date;
|
||||||
|
};
|
||||||
|
|
||||||
|
const events = [
|
||||||
|
{ start: createDate(9, 30), end: createDate(11, 20), color: '#33B679', title: 'PAAS' },
|
||||||
|
{ start: createDate(12, 30), end: createDate(1, 20), color: '#33B679', title: 'STAT' },
|
||||||
|
{ start: createDate(1, 30), end: createDate(2, 20), color: '#33B679', title: 'MATH' }
|
||||||
|
// { start: createDate(9, 30), end: createDate(11, 20), color: '#59acff', title: 'Work' },
|
||||||
|
// { start: createDate(12, 30), end: createDate(1, 20), color: '#59acff', title: 'Walk' },
|
||||||
|
// { start: createDate(1, 30), end: createDate(2, 20), color: '#59acff', title: 'Bike' }
|
||||||
|
];
|
||||||
|
|
||||||
|
function getTime() {
|
||||||
|
try { return tizen.time.getCurrentDateTime(); }
|
||||||
|
catch (e) { return new Date(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCircle(angle, distance, width, color) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||||
|
ctx.rotate(angle)
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(distance, 0, width / 2, 0, 2 * Math.PI, false);
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fill();
|
||||||
|
ctx.closePath();
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
// function drawLine(startDistance, endDistance, angle, width, color) {
|
||||||
|
// ctx.save();
|
||||||
|
// ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||||
|
// ctx.rotate(degToRad(angle + 90));
|
||||||
|
|
||||||
|
// ctx.beginPath();
|
||||||
|
// ctx.lineWidth = width;
|
||||||
|
// ctx.strokeStyle = color;
|
||||||
|
// ctx.moveTo(startDistance, 0);
|
||||||
|
// ctx.lineTo(endDistance, 0);
|
||||||
|
// ctx.stroke();
|
||||||
|
// ctx.closePath();
|
||||||
|
|
||||||
|
// ctx.restore();
|
||||||
|
// }
|
||||||
|
|
||||||
|
function drawRoundedRect(startDistance, endDistance, angle, width, fill, stroke, strokeWidth) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||||
|
ctx.rotate(angle);
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(-width / 2, startDistance);
|
||||||
|
ctx.lineTo(-width / 2, endDistance);
|
||||||
|
ctx.quadraticCurveTo(-width / 2, endDistance + width / 1.5, 0, endDistance + width / 1.5);
|
||||||
|
ctx.quadraticCurveTo(width / 2, endDistance + width / 1.5, width / 2, endDistance);
|
||||||
|
ctx.lineTo(width / 2, startDistance);
|
||||||
|
ctx.quadraticCurveTo(width / 2, startDistance - width / 1.5, 0, startDistance - width / 1.5);
|
||||||
|
ctx.quadraticCurveTo(-width / 2, startDistance - width / 1.5, -width / 2, startDistance);
|
||||||
|
ctx.closePath();
|
||||||
|
|
||||||
|
if (fill) {
|
||||||
|
ctx.fillStyle = fill;
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stroke && strokeWidth) {
|
||||||
|
ctx.strokeStyle = stroke;
|
||||||
|
ctx.lineWidth = strokeWidth;
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawEvent(event) {
|
||||||
|
const startAngle = degToRad(((event.start.getHours() % 12) +
|
||||||
|
event.start.getMinutes() / 60) / 12 * 360 + EVENT_WIDTH / 6 - 90);
|
||||||
|
const endAngle = degToRad(((event.end.getHours() % 12) +
|
||||||
|
event.end.getMinutes() / 60) / 12 * 360 - EVENT_WIDTH / 8 - 90);
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.fillStyle = COLOR_LIGHT_DIM;
|
||||||
|
|
||||||
|
ctx.arc(0, 0, clockRadius - EVENT_BUFFER, startAngle, endAngle, false);
|
||||||
|
|
||||||
|
let controlPosX = Math.cos(endAngle + degToRad(4.25)) * (clockRadius - EVENT_BUFFER);
|
||||||
|
let controlPosY = Math.sin(endAngle + degToRad(4.25)) * (clockRadius - EVENT_BUFFER);
|
||||||
|
let endPosX = Math.cos(endAngle + degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH / 2);
|
||||||
|
let endPosY = Math.sin(endAngle + degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH / 2);
|
||||||
|
|
||||||
|
ctx.quadraticCurveTo(controlPosX, controlPosY, endPosX, endPosY);
|
||||||
|
|
||||||
|
controlPosX = Math.cos(endAngle + degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH);
|
||||||
|
controlPosY = Math.sin(endAngle + degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH);
|
||||||
|
endPosX = Math.cos(endAngle) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH);
|
||||||
|
endPosY = Math.sin(endAngle) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH);
|
||||||
|
|
||||||
|
ctx.quadraticCurveTo(controlPosX, controlPosY, endPosX, endPosY);
|
||||||
|
|
||||||
|
ctx.arc(0, 0, clockRadius - EVENT_BUFFER - EVENT_WIDTH, endAngle, startAngle, true);
|
||||||
|
|
||||||
|
controlPosX = Math.cos(startAngle - degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH);
|
||||||
|
controlPosY = Math.sin(startAngle - degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH);
|
||||||
|
endPosX = Math.cos(startAngle - degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH / 2);
|
||||||
|
endPosY = Math.sin(startAngle - degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH / 2);
|
||||||
|
|
||||||
|
ctx.quadraticCurveTo(controlPosX, controlPosY, endPosX, endPosY);
|
||||||
|
|
||||||
|
controlPosX = Math.cos(startAngle - degToRad(4.25)) * (clockRadius - EVENT_BUFFER);
|
||||||
|
controlPosY = Math.sin(startAngle - degToRad(4.25)) * (clockRadius - EVENT_BUFFER);
|
||||||
|
endPosX = Math.cos(startAngle) * (clockRadius - EVENT_BUFFER);
|
||||||
|
endPosY = Math.sin(startAngle) * (clockRadius - EVENT_BUFFER);
|
||||||
|
|
||||||
|
ctx.quadraticCurveTo(controlPosX, controlPosY, endPosX, endPosY);
|
||||||
|
|
||||||
|
ctx.fill();
|
||||||
|
ctx.closePath();
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
drawCircle(startAngle, clockRadius - EVENT_BUFFER - EVENT_WIDTH / 2, EVENT_WIDTH - 8, event.color);
|
||||||
|
|
||||||
|
ctx.font = `900 ${TITLE_SIZE}px Arial sans-serif`;
|
||||||
|
ctx.fillStyle = '#fff';
|
||||||
|
ctx.textBaseline = 'top';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
|
||||||
|
let currentAngle = startAngle + degToRad(96);
|
||||||
|
for (let i = 0; i < event.title.length; i++) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||||
|
ctx.rotate(currentAngle);
|
||||||
|
|
||||||
|
ctx.fillText(event.title[i], 0, -(clockRadius - EVENT_BUFFER - TITLE_BUFFER));
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
if (i < event.title.length - 1)
|
||||||
|
currentAngle += ctx.measureText(event.title[i]).width * 0.0065 / 2
|
||||||
|
+ ctx.measureText(event.title[i + 1]).width * 0.0065 / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGlow(offsetX, offsetY, scaleMult, color) {
|
||||||
|
glow_ctx.fillStyle = color;
|
||||||
|
glow_ctx.globalCompositeOperation = 'source-over';
|
||||||
|
glow_ctx.fillRect(0, 0, glow_canvas.width, glow_canvas.height);
|
||||||
|
glow_ctx.globalCompositeOperation = 'destination-in';
|
||||||
|
glow_ctx.drawImage(glow_image, 0, 0);
|
||||||
|
|
||||||
|
const scale = 240 * scaleMult;
|
||||||
|
|
||||||
|
ctx.drawImage(glow_canvas,
|
||||||
|
canvas.width / 2 + offsetX - scale / 2,
|
||||||
|
canvas.height / 2 + offsetY - scale / 2,
|
||||||
|
scale, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawWatchFace() {
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
const time = getTime();
|
||||||
|
|
||||||
|
ctx.font = `900 ${DATE_SIZE}px Arial sans-serif`;
|
||||||
|
ctx.fillStyle = '#aaa';
|
||||||
|
ctx.textBaseline = 'bottom';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
|
||||||
|
ctx.fillText(`${['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'][time.getDay()]} ${time.getDate()}`,
|
||||||
|
clockRadius, clockRadius + clockRadius / 1.75);
|
||||||
|
|
||||||
|
ctx.fillStyle = '#666'
|
||||||
|
ctx.fillRect(clockRadius - 1 * DATE_SIZE, clockRadius + clockRadius / 1.75 + 2, 2 * DATE_SIZE, 2);
|
||||||
|
|
||||||
|
if (!ambient) {
|
||||||
|
rot = (rot + 1) % 360;
|
||||||
|
|
||||||
|
let scaleA = 1 + (Math.sin(degToRad(rot + 180)) / 6);
|
||||||
|
let scaleB = 1 + (Math.cos(degToRad(rot)) / 6);
|
||||||
|
let scaleC = 1.3 + (Math.cos(degToRad(rot - 90)) / 6);
|
||||||
|
let offsetX = Math.cos(degToRad(rot + 90)) * 50 + (1 - scaleA);
|
||||||
|
let offsetY = Math.sin(degToRad(rot + 90)) * 50 + (1 - scaleB);
|
||||||
|
|
||||||
|
if (theme.glow) {
|
||||||
|
ctx.globalCompositeOperation = 'lighter';
|
||||||
|
drawGlow(-offsetX, -offsetY, scaleA, theme.glow_b);
|
||||||
|
drawGlow(0, 0, scaleC, theme.glow_a);
|
||||||
|
drawGlow(offsetX, offsetY, scaleB, theme.glow_c);
|
||||||
|
ctx.globalCompositeOperation = 'source-over';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme.notches) {
|
||||||
|
for (let i = 0; i < 12 * 5; i++) {
|
||||||
|
if (i % 15 === 0)
|
||||||
|
drawCircle(degToRad(i / (12 * 5) * 360 - 90), clockRadius - NOTCH_BUFFER - 4, 8, COLOR_MINUTE_HAND)
|
||||||
|
else if (i % 5 === 0 && theme.notches >= 2)
|
||||||
|
drawCircle(degToRad(i / (12 * 5) * 360 - 90), clockRadius - NOTCH_BUFFER - 4, 6, COLOR_LIGHT_OVERLAY)
|
||||||
|
else if (theme.notches >= 3)
|
||||||
|
drawCircle(degToRad(i / (12 * 5) * 360 - 90), clockRadius - NOTCH_BUFFER - 4, 4, COLOR_LIGHT_DIM)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme.events) {
|
||||||
|
for (let event of events) {
|
||||||
|
drawEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawCircle(0, 0, 36, COLOR_LIGHT_OVERLAY);
|
||||||
|
drawCircle(0, 0, 20, '#fff');
|
||||||
|
|
||||||
|
if (theme.minutes) {
|
||||||
|
const minuteAngle = degToRad((time.getMinutes() + time.getSeconds() / 60) / 60 * 360 - 180);
|
||||||
|
drawRoundedRect(34, clockRadius - 64, minuteAngle, 16, COLOR_MINUTE_HAND);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hourAngle = degToRad(((time.getHours() % 12) + time.getMinutes() / 60) / 12 * 360 - 180);
|
||||||
|
drawRoundedRect(36, clockRadius - 80, hourAngle, 16, 'rgba(0, 0, 0, 0.15)', '#fff', 4);
|
||||||
|
|
||||||
|
if (!ambient) setTimeout(() => window.requestAnimationFrame(drawWatchFace), 1000/10);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.requestAnimationFrame(drawWatchFace);
|
||||||
|
|
||||||
|
window.addEventListener('timetick', drawWatchFace);
|
||||||
|
|
||||||
|
window.addEventListener('ambientmodechanged', (e) => {
|
||||||
|
ambient = e.detail.ambientMode;
|
||||||
|
drawWatchFace();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
if (!document.hidden) drawWatchFace();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
BIN
watch/res/glow.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
1
watch/res/icon.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="#fff" d="M6,17C6,15 10,13.9 12,13.9C14,13.9 18,15 18,17V18H6M15,9A3,3 0 0,1 12,12A3,3 0 0,1 9,9A3,3 0 0,1 12,6A3,3 0 0,1 15,9M3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3H5C3.89,3 3,3.9 3,5Z" /></svg>
|
After Width: | Height: | Size: 502 B |
1
watch/version.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
1.0.5
|