Monorepo initial commit
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"e997a5256149a4b76e6bfd6cbf519c5e5a0f1d278a3d8fa1253022b03c90473b": true,
|
||||
"af683c96e0ffd2cf81287651c9433fa44debc1220ca7cb431fe482747f34a505": true,
|
||||
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
|
||||
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
|
||||
}
|
|
@ -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
|
|
@ -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",
|
||||
],
|
||||
)
|
|
@ -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)
|
|
@ -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,
|
||||
)
|
|
@ -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:
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 5.5 KiB |
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 |
|
@ -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>
|
|
@ -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' }
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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" "$@"
|
|
@ -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
|
|
@ -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'
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 46 KiB |
|
@ -0,0 +1,7 @@
|
|||
module.exports = function(api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
presets: ['babel-preset-expo'],
|
||||
plugins: ['react-native-reanimated/plugin'],
|
||||
};
|
||||
};
|
|
@ -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);
|
|
@ -0,0 +1,4 @@
|
|||
// Learn more https://docs.expo.io/guides/customizing-metro
|
||||
const { getDefaultConfig } = require('expo/metro-config');
|
||||
|
||||
module.exports = getDefaultConfig(__dirname);
|
|
@ -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"
|
||||
}
|
|
@ -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
|
||||
}
|
||||
});
|
|
@ -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',
|
||||
// },
|
||||
// });
|
|
@ -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}/>;
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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' }]} />;
|
||||
// }
|
|
@ -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} />;
|
||||
// }
|
|
@ -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,
|
||||
// }
|
||||
// };
|
|
@ -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,
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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'
|
||||
}
|
||||
});
|
|
@ -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
|
||||
},
|
||||
});
|
|
@ -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%',
|
||||
},
|
||||
});
|
|
@ -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',
|
||||
}
|
||||
});
|
|
@ -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';
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"extends": "expo/tsconfig.base",
|
||||
"compilerOptions": {
|
||||
"strict": true
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
>;
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
Window
|
|
@ -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==
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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.
|
||||
|
|
@ -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.
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -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>
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
})();
|
After Width: | Height: | Size: 7.9 KiB |
|
@ -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 |
|
@ -0,0 +1 @@
|
|||
1.0.5
|