├── app
├── .gitignore
├── debug.jks
├── src
│ └── main
│ │ ├── res
│ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── drawable
│ │ │ ├── morph_nine_five.xml
│ │ │ ├── io_number_0.xml
│ │ │ ├── io_number_1.xml
│ │ │ ├── io_number_2.xml
│ │ │ ├── io_number_3.xml
│ │ │ ├── io_number_4.xml
│ │ │ ├── io_number_6.xml
│ │ │ ├── io_number_7.xml
│ │ │ ├── io_number_8.xml
│ │ │ ├── io_number_5.xml
│ │ │ ├── io_number_9.xml
│ │ │ ├── ic_google_io_2016.xml
│ │ │ ├── io_2_drawable_avd_hash_9016.xml
│ │ │ └── io_1_drawable_ic_hash_io16.xml
│ │ ├── anim
│ │ │ └── path_morph.xml
│ │ ├── values-w820dp
│ │ │ └── dimens.xml
│ │ ├── values
│ │ │ ├── dimens.xml
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ ├── mini_drawer.xml
│ │ │ ├── styles.xml
│ │ │ ├── attrs.xml
│ │ │ ├── io_3_values_hash_io16.xml
│ │ │ ├── svg_paths.xml
│ │ │ └── colors_material.xml
│ │ ├── animator
│ │ │ ├── offset_trim_one.xml
│ │ │ └── offset_trim_six.xml
│ │ ├── values-night
│ │ │ └── styles.xml
│ │ └── layout
│ │ │ ├── credits_container.xml
│ │ │ ├── activity_main.xml
│ │ │ └── content_main.xml
│ │ ├── java
│ │ └── im
│ │ │ └── ene
│ │ │ └── lab
│ │ │ └── io_timer
│ │ │ ├── util
│ │ │ ├── techniques
│ │ │ │ ├── AbstractFillMode.java
│ │ │ │ ├── NWAlignment.java
│ │ │ │ └── BaseFillMode.java
│ │ │ ├── SVGParser.java
│ │ │ ├── Number.java
│ │ │ ├── AnimationUtil.java
│ │ │ ├── VectAlign.java
│ │ │ ├── NumberDrawer.java
│ │ │ ├── PathDataSource.java
│ │ │ ├── PathNodeUtils.java
│ │ │ └── PathParser.java
│ │ │ ├── IoTimer.java
│ │ │ └── ui
│ │ │ ├── widget
│ │ │ ├── GoogleIO2016NumberView.java
│ │ │ └── ForegroundLinearLayout.java
│ │ │ └── MainActivity.java
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── .idea
├── .name
├── dictionaries
│ └── eneim.xml
├── copyright
│ ├── profiles_settings.xml
│ ├── vectalign.xml
│ └── eneim.xml
└── codeStyleSettings.xml
├── settings.gradle
├── art
├── screen_record.gif
└── web_hi_res_512.png
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties-sample
├── gradlew.bat
├── README.md
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | Google_io_2016_timer
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/app/debug.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eneim/Google_io_2016_timer/HEAD/app/debug.jks
--------------------------------------------------------------------------------
/art/screen_record.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eneim/Google_io_2016_timer/HEAD/art/screen_record.gif
--------------------------------------------------------------------------------
/art/web_hi_res_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eneim/Google_io_2016_timer/HEAD/art/web_hi_res_512.png
--------------------------------------------------------------------------------
/.idea/dictionaries/eneim.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eneim/Google_io_2016_timer/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eneim/Google_io_2016_timer/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eneim/Google_io_2016_timer/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eneim/Google_io_2016_timer/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eneim/Google_io_2016_timer/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eneim/Google_io_2016_timer/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /app/src/androidTest
2 | /app/src/test
3 | /.idea/compiler.xml
4 | /.idea/findbugs-idea.xml
5 | /.idea/gradle.xml
6 | /.idea/runConfigurations.xml
7 | /.idea/misc.xml
8 | /.idea/modules.xml
9 | /.idea/vcs.xml
10 | /.idea/inspectionProfiles
11 | /.idea/encodings.xml
12 | /gradle.properties
13 | *.iml
14 | .gradle
15 | /local.properties
16 | /.idea/workspace.xml
17 | /.idea/libraries
18 | .DS_Store
19 | /build
20 | /captures
21 |
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/util/techniques/AbstractFillMode.java:
--------------------------------------------------------------------------------
1 | package im.ene.lab.io_timer.util.techniques;
2 |
3 | import im.ene.lab.io_timer.util.PathParser;
4 | import java.util.ArrayList;
5 |
6 | /**
7 | * Created by ziby on 07/08/15.
8 | */
9 | public abstract class AbstractFillMode {
10 |
11 | public abstract void fillInjectedNodes(ArrayList from,
12 | ArrayList to);
13 | }
14 |
--------------------------------------------------------------------------------
/.idea/copyright/vectalign.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/copyright/eneim.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/morph_nine_five.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/path_morph.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/io_number_0.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/io_number_1.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/io_number_2.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/io_number_3.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/io_number_4.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/io_number_6.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/io_number_7.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/io_number_8.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
26 |
27 |
--------------------------------------------------------------------------------
/gradle.properties-sample:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 | fabric_api_key = FABRIC_KEY
20 |
21 | # Android Keystore
22 | io_prodKeyAlias = alias
23 | io_prodKeyPassword = alias_pass
24 | io_prodStoreFile = key_path
25 | io_prodStorePassword = key_pass
--------------------------------------------------------------------------------
/app/src/main/res/drawable/io_number_5.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/io_number_9.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
21 | 64dp
22 | 128dp
23 | 366dp
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 | 16dp
20 | 16dp
21 |
22 | 4dp
23 | 8dp
24 |
25 | 96dp
26 |
27 | 114dp
28 | 244dp
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/IoTimer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 eneim@Eneim Labs, nam@ene.im
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package im.ene.lab.io_timer;
18 |
19 | import android.app.Application;
20 | import com.crashlytics.android.Crashlytics;
21 | import com.jakewharton.threetenabp.AndroidThreeTen;
22 | import io.fabric.sdk.android.Fabric;
23 |
24 | /**
25 | * Created by eneim on 3/4/16.
26 | */
27 | public class IoTimer extends Application {
28 |
29 | @Override public void onCreate() {
30 | super.onCreate();
31 | Fabric.with(this, new Crashlytics());
32 | AndroidThreeTen.init(this);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/res/animator/offset_trim_one.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/animator/offset_trim_six.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | #66000000
20 |
21 | #78909C
22 | #EF5350
23 | #5C6BC0
24 | #26C6DA
25 | #8CF2F2
26 |
27 | @color/io_color_main
28 | @color/io_color_main
29 | #FF4081
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | IO When
19 |
20 | 2016-05-18T09:30:00-07:00
21 | Vector source (SVG data) for numbers were extracted from Google IO event website.
22 | Vector source (SVG data) was converted to Java helper class (NumberDrawer in this App) by https://codecrafted.net/svgtoandroid (with my manipulation later) to give support for lower Android versions.
23 | Sidebar animations was created by Nick Butcher (@crafty).
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/values/mini_drawer.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
28 |
29 | 4dp
30 | ?attr/actionBarSize
31 | 300dp
32 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
28 |
29 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
28 |
29 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_google_io_2016.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
23 |
27 |
31 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/values/io_3_values_hash_io16.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 |
21 |
22 | M302.14,60.72C302.29,61.46 276.46,97.06 270.1,112.55C260.87,138.44 278.6,149.83 284.3,152.76C299.15,160.38 316.85,150.27 323.08,141.43C329.3,132.59 333.05,109.99 316.85,100.57C306.85,94.75 290.54,97.32 290.2,97.06C289.85,96.79 276.32,81.31 276.46,80.88C276.6,80.45 294.73,77.62 302.88,84.28C315.76,92.99 315.62,114.99 306.84,127.42C298.06,139.85 276.46,144.38 260.54,130.73C238.46,111.79 259.06,85.64 260.87,83.1C260.87,83.1 286.23,46.19 286.83,46.03C287.43,45.87 301.99,59.99 302.14,60.72Z
23 | M211,45L235,45A2,2 0,0 1,237 47L237,155A2,2 0,0 1,235 157L211,157A2,2 0,0 1,209 155L209,47A2,2 0,0 1,211 45z
24 | - 5
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 |
21 |
22 |
23 |
30 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/io_2_drawable_avd_hash_9016.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
21 |
22 |
25 |
26 |
29 |
30 |
33 |
34 |
37 |
38 |
41 |
42 |
45 |
46 |
49 |
50 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/credits_container.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
23 |
24 |
30 |
31 |
37 |
38 |
44 |
45 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/util/SVGParser.java:
--------------------------------------------------------------------------------
1 | package im.ene.lab.io_timer.util;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import javax.xml.parsers.DocumentBuilder;
6 | import javax.xml.parsers.DocumentBuilderFactory;
7 | import javax.xml.parsers.ParserConfigurationException;
8 | import org.w3c.dom.Document;
9 | import org.w3c.dom.Element;
10 | import org.w3c.dom.NodeList;
11 | import org.xml.sax.SAXException;
12 |
13 | /**
14 | * Created by ziby on 26/08/15.
15 | */
16 | public class SVGParser {
17 |
18 | /**
19 | * Check if file is an SVG
20 | */
21 | public static boolean isSVGImage(File f) {
22 | try {
23 | System.out.println("Checking if " + f.getAbsolutePath() + " is an SVG file...");
24 | Document document = getXMLDocumentFromFile(f);
25 | boolean headerSVG = document.getDoctype() != null && document.getDoctype()
26 | .getName()
27 | .toLowerCase()
28 | .equals("svg");
29 | if (headerSVG) {
30 | return true;
31 | } else {
32 | return document.getElementsByTagName("svg") != null;
33 | }
34 | } catch (Exception e) {
35 | }
36 |
37 | return false;
38 | }
39 |
40 | /**
41 | * Parse path elements of an SVG file into a single big path sequence.
42 | */
43 | public static String getPathDataFromSVGFile(File f) {
44 | try {
45 | StringBuilder sb = new StringBuilder();
46 | Document document = getXMLDocumentFromFile(f);
47 | NodeList nList = document.getElementsByTagName("path");
48 | for (int i = 0; i < nList.getLength(); i++) {
49 | Element e = (Element) nList.item(i);
50 | sb.append(e.getAttribute("d"));
51 | }
52 |
53 | return sb.toString();
54 | } catch (Exception e) {
55 | }
56 |
57 | return null;
58 | }
59 |
60 | private static Document getXMLDocumentFromFile(File f)
61 | throws ParserConfigurationException, IOException, SAXException {
62 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
63 | factory.setNamespaceAware(false);
64 | factory.setValidating(false);
65 | factory.setFeature("http://xml.org/sax/features/namespaces", false);
66 | factory.setFeature("http://xml.org/sax/features/validation", false);
67 | factory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
68 | factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
69 | DocumentBuilder builder = null;
70 | builder = factory.newDocumentBuilder();
71 | Document document = builder.parse(f);
72 | return document;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/util/Number.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 eneim@Eneim Labs, nam@ene.im
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package im.ene.lab.io_timer.util;
18 |
19 | import android.util.Pair;
20 | import java.util.Map;
21 | import java.util.concurrent.ConcurrentHashMap;
22 |
23 | /**
24 | * Created by eneim on 3/4/16.
25 | */
26 | public enum Number {
27 | ZERO(PathDataSource.SRC_ZERO),
28 | ONE(PathDataSource.SRC_ONE),
29 | TWO(PathDataSource.SRC_TWO),
30 | THREE(PathDataSource.SRC_THREE),
31 | FOUR(PathDataSource.SRC_FOUR),
32 | FIVE(PathDataSource.SRC_FIVE),
33 | SIX(PathDataSource.SRC_SIX),
34 | SEVEN(PathDataSource.SRC_SEVEN),
35 | EIGHT(PathDataSource.SRC_EIGHT),
36 | NINE(PathDataSource.SRC_NINE);
37 |
38 | public static final Number[] VALUES = {
39 | ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE
40 | };
41 |
42 | private static final Map sCache = new ConcurrentHashMap<>();
43 | private static final Map, Pair> sPairs =
44 | new ConcurrentHashMap<>();
45 | final String pathData;
46 |
47 | Number(String pathData) {
48 | this.pathData = pathData;
49 | }
50 |
51 | public static PathParser.PathDataNode[] getNodes(Number number) {
52 | PathParser.PathDataNode[] nodes = sCache.get(number);
53 | if (nodes == null) {
54 | nodes = PathParser.createNodesFromPathData(number.pathData);
55 | sCache.put(number, nodes);
56 | }
57 |
58 | return nodes;
59 | }
60 |
61 | public static Pair alignNumbers(Pair source) {
62 | Pair data = sPairs.get(source);
63 | if (data == null) {
64 | String[] temp =
65 | VectAlign.align(source.first.pathData, source.second.pathData, VectAlign.Mode.BASE);
66 | if (temp == null || temp.length != 2) {
67 | throw new IllegalStateException("Something is wrong here!");
68 | }
69 |
70 | data = new Pair<>(temp[0], temp[1]);
71 | sPairs.put(source, data);
72 | }
73 |
74 | return data;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # IO When - Google IO 2016 Countdown App.
2 |
3 | Featuring funny animation, just like (and close to) the event homepage.
4 |
5 |
6 |
7 | ## TL, DR
8 |
9 | > See [https://events.google.com/io2016/](https://events.google.com/io2016/) then see this repo's screen cap.
10 |
11 | > **NOTE**: this Application was suspended by Play Store by violating ***'the impersonation policy'***. I have no time as well as no will to fix it again. So please use this as your own risk, but don't publish it to store again.
12 |
13 | ## IO When in action
14 |
15 |
16 |
17 | ## How to get the apk up and running
18 |
19 | - Fork, clone this then import it into Android Studio 2.0 beta 6 (I developed this on AS 2.0).
20 | - Connect your device, then from terminal, run ```./gradlew installDebug```
21 | - Check your device. The app should be ready to use.
22 |
23 | ## Use this code base, contribute
24 |
25 | - Rename ```gradle.properties-sample``` to ```gradle.properties```.
26 | - Change requested value to your own (fabric key, keystore values).
27 | - Pull request.
28 |
29 | ## Used libraries
30 |
31 | - AppCompat 23.2.0
32 | - [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP)
33 | - [RxAndroid](https://github.com/ReactiveX/RxAndroid)
34 | - [butterknife](jakewharton.github.io/butterknife/)
35 | - [vectalign](https://github.com/bonnyfone/vectalign)
36 | - [svgtoandroid](https://codecrafted.net/svgtoandroid)
37 | - animated io 2016 from [Nick Butcher'gist](https://gist.github.com/eneim/775be95fe9baca673fab)
38 |
39 | ## LICENSE
40 |
41 | Copyright 2016 eneim@Eneim Labs, nam@ene.im
42 |
43 | Licensed under the Apache License, Version 2.0 (the "License");
44 | you may not use this file except in compliance with the License.
45 | You may obtain a copy of the License at
46 |
47 | http://www.apache.org/licenses/LICENSE-2.0
48 |
49 | Unless required by applicable law or agreed to in writing, software
50 | distributed under the License is distributed on an "AS IS" BASIS,
51 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
52 | See the License for the specific language governing permissions and
53 | limitations under the License.
54 |
55 | ## Support on Beerpay
56 | Hey dude! Help me out for a couple of :beers:!
57 |
58 | [](https://beerpay.io/eneim/Google_io_2016_timer) [](https://beerpay.io/eneim/Google_io_2016_timer?focus=wish)
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/util/AnimationUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 eneim@Eneim Labs, nam@ene.im
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package im.ene.lab.io_timer.util;
18 |
19 | import android.animation.TypeEvaluator;
20 |
21 | /**
22 | * Created by eneim on 3/4/16.
23 | */
24 | public class AnimationUtil {
25 |
26 | /**
27 | * PathDataEvaluator is used to interpolate between two paths which are
28 | * represented in the same format but different control points' values.
29 | * The path is represented as an array of PathDataNode here, which is
30 | * fundamentally an array of floating point numbers.
31 | */
32 | public static class PathDataEvaluator implements TypeEvaluator {
33 | private PathParser.PathDataNode[] mNodeArray;
34 |
35 | /**
36 | * Create a PathParser.PathDataNode[] that does not reuse the animated value.
37 | * Care must be taken when using this option because on every evaluation
38 | * a new PathParser.PathDataNode[] will be allocated.
39 | */
40 | private PathDataEvaluator() {
41 | }
42 |
43 | /**
44 | * Create a PathDataEvaluator that reuses nodeArray for every evaluate() call.
45 | * Caution must be taken to ensure that the value returned from
46 | * {@link android.animation.ValueAnimator#getAnimatedValue()} is not cached, modified, or
47 | * used across threads. The value will be modified on each evaluate() call.
48 | *
49 | * @param nodeArray The array to modify and return from evaluate.
50 | */
51 | public PathDataEvaluator(PathParser.PathDataNode[] nodeArray) {
52 | mNodeArray = nodeArray;
53 | }
54 |
55 | @Override public PathParser.PathDataNode[] evaluate(float fraction,
56 | PathParser.PathDataNode[] startPathData, PathParser.PathDataNode[] endPathData) {
57 | if (!PathParser.canMorph(startPathData, endPathData)) {
58 | throw new IllegalArgumentException(
59 | "Can't interpolate between" + " two incompatible pathData");
60 | }
61 |
62 | if (mNodeArray == null || !PathParser.canMorph(mNodeArray, startPathData)) {
63 | mNodeArray = PathParser.deepCopyNodes(startPathData);
64 | }
65 |
66 | for (int i = 0; i < startPathData.length; i++) {
67 | mNodeArray[i].interpolatePathDataNode(startPathData[i], endPathData[i], fraction);
68 | }
69 |
70 | return mNodeArray;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/eneim/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 | # This is a configuration file for ProGuard.
19 | # http://proguard.sourceforge.net/index.html#manual/usage.html
20 | -dontusemixedcaseclassnames
21 | -dontskipnonpubliclibraryclasses
22 | -verbose
23 | # Optimization is turned off by default. Dex does not like code run
24 | # through the ProGuard optimize and preverify steps (and performs some
25 | # of these optimizations on its own).
26 | #-dontoptimize
27 | -dontpreverify
28 | #-dontobfuscate
29 | # 2015-12-01
30 | -optimizationpasses 12
31 | -repackageclasses ''
32 | -allowaccessmodification
33 | -optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
34 | #-optimizations !code/simplification/arithmetic
35 |
36 | -keepattributes *Annotation*
37 | -keepattributes Signature
38 |
39 | -keep,allowobfuscation,allowoptimization public class * extends android.app.Activity
40 | -keep public class * extends android.app.Application
41 |
42 | -keep public class * extends android.view.View {
43 | public (android.content.Context);
44 | public (android.content.Context, android.util.AttributeSet);
45 | public (android.content.Context, android.util.AttributeSet, int);
46 | }
47 |
48 | -keepnames class * extends java.lang.Throwable
49 |
50 | -keepclasseswithmembers class * {
51 | public (android.content.Context, android.util.AttributeSet);
52 | }
53 |
54 | -keepclasseswithmembers class * {
55 | public (android.content.Context, android.util.AttributeSet, int);
56 | }
57 |
58 | -keepclassmembers class * extends android.content.Context {
59 | public void *(android.view.View);
60 | public void *(android.view.MenuItem);
61 | }
62 |
63 | -keepclassmembers class * implements android.os.Parcelable {
64 | static ** CREATOR;
65 | }
66 |
67 | -keepnames class * implements android.os.Parcelable {
68 | public static final ** CREATOR;
69 | }
70 |
71 | -keepclassmembers class **.R$* {
72 | public static ;
73 | }
74 |
75 | -dontwarn sun.misc.**
76 |
77 | -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
78 | long producerIndex;
79 | long consumerIndex;
80 | }
81 |
82 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
83 | rx.internal.util.atomic.LinkedQueueNode producerNode;
84 | }
85 |
86 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
87 | rx.internal.util.atomic.LinkedQueueNode consumerNode;
88 | }
89 |
90 | -keep class butterknife.** { *; }
91 | -dontwarn butterknife.internal.**
92 | -keep class **$$ViewBinder { *; }
93 |
94 | -keepclasseswithmembernames class * {
95 | @butterknife.* ;
96 | }
97 |
98 | -keepclasseswithmembernames class * {
99 | @butterknife.* ;
100 | }
101 |
102 | # -keep class com.crashlytics.** { *; }
103 | # -dontwarn com.crashlytics.**
104 | -keepattributes SourceFile,LineNumberTable,*Annotation*
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | maven { url 'https://maven.fabric.io/public' }
4 | }
5 |
6 | dependencies {
7 | classpath 'io.fabric.tools:gradle:1.+'
8 | }
9 | }
10 | apply plugin: 'com.android.application'
11 | apply plugin: 'io.fabric'
12 |
13 | repositories {
14 | maven { url 'https://maven.fabric.io/public' }
15 | }
16 |
17 | /*
18 | * Copyright 2016 eneim@Eneim Labs, nam@ene.im
19 | *
20 | * Licensed under the Apache License, Version 2.0 (the "License");
21 | * you may not use this file except in compliance with the License.
22 | * You may obtain a copy of the License at
23 | *
24 | * http://www.apache.org/licenses/LICENSE-2.0
25 | *
26 | * Unless required by applicable law or agreed to in writing, software
27 | * distributed under the License is distributed on an "AS IS" BASIS,
28 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29 | * See the License for the specific language governing permissions and
30 | * limitations under the License.
31 | */
32 |
33 | import java.text.DateFormat
34 | import java.text.SimpleDateFormat
35 |
36 | def dateTimeInt() {
37 | DateFormat df = new SimpleDateFormat("yyyyMMdd");
38 | return Integer.parseInt(df.format(new Date()));
39 | }
40 |
41 | def dateTimeString() {
42 | DateFormat df = new SimpleDateFormat("yyyy.MM.dd");
43 | return df.format(new Date());
44 | }
45 |
46 | android {
47 | compileSdkVersion 23
48 | buildToolsVersion "23.0.3"
49 |
50 | signingConfigs {
51 | release {
52 | keyAlias io_prodKeyAlias
53 | keyPassword io_prodKeyPassword
54 | storeFile file(io_prodStoreFile)
55 | storePassword io_prodStorePassword
56 | }
57 |
58 | debug {
59 | keyAlias "debug"
60 | keyPassword "android"
61 | storeFile file("debug.jks")
62 | storePassword "android"
63 | }
64 | }
65 |
66 | defaultConfig {
67 | applicationId "im.ene.lab.io_timer"
68 | minSdkVersion 16
69 | targetSdkVersion 23
70 | versionCode dateTimeInt()
71 | versionName dateTimeString()
72 | archivesBaseName = "io_timer-${versionName}"
73 | manifestPlaceholders = [fabric_api_key: "${fabric_api_key}"]
74 |
75 | vectorDrawables.useSupportLibrary = true
76 | }
77 |
78 | buildTypes {
79 | release {
80 | signingConfig signingConfigs.release
81 | minifyEnabled true
82 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
83 | }
84 |
85 | debug {
86 | signingConfig signingConfigs.debug
87 | applicationIdSuffix ".debug"
88 | minifyEnabled false
89 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
90 | }
91 | }
92 |
93 | packagingOptions {
94 | exclude 'META-INF/services/javax.annotation.processing.Processor'
95 | }
96 |
97 | lintOptions {
98 | disable 'InvalidPackage'
99 | }
100 | }
101 |
102 | ext {
103 | support_lib_version = '23.2.1'
104 | }
105 |
106 | dependencies {
107 | compile fileTree(dir: 'libs', include: ['*.jar'])
108 | testCompile 'junit:junit:4.12'
109 | compile 'com.android.support:appcompat-v7:23.2.1'
110 |
111 | compile 'io.reactivex:rxandroid:1.1.0'
112 | // Because RxAndroid releases are few and far between, it is recommended you also
113 | // explicitly depend on RxJava's latest version for bug fixes and new features.
114 | compile 'io.reactivex:rxjava:1.1.1'
115 |
116 | compile 'com.jakewharton.threetenabp:threetenabp:1.0.3'
117 | compile 'com.jakewharton:butterknife:7.0.1'
118 | compile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar') {
119 | transitive = true;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/util/VectAlign.java:
--------------------------------------------------------------------------------
1 | package im.ene.lab.io_timer.util;
2 |
3 | import im.ene.lab.io_timer.util.techniques.AbstractFillMode;
4 | import im.ene.lab.io_timer.util.techniques.BaseFillMode;
5 | import im.ene.lab.io_timer.util.techniques.NWAlignment;
6 | import java.util.ArrayList;
7 | import java.util.Arrays;
8 |
9 | /**
10 | * Class used to align VectorDrawable sequences in order to allow morphing animations between them.
11 | */
12 | public final class VectAlign {
13 |
14 | private static final int MAX_ALIGN_ITERATIONS = 5;
15 |
16 | /**
17 | * Alignment technique
18 | */
19 | public enum Mode {
20 | /**
21 | * Inject necessary elements by repeating existing ones
22 | */
23 | BASE(1);
24 |
25 | //TODO more technique
26 |
27 | private int mode;
28 |
29 | Mode(int mode) {
30 | this.mode = mode;
31 | }
32 | }
33 |
34 | /**
35 | * @param from The source path represented in a String
36 | * @param to The target path represented in a String
37 | * @return whether the from can morph into to
38 | */
39 | public static boolean canMorph(String from, String to) {
40 | return PathParser.canMorph(PathParser.createNodesFromPathData(from),
41 | PathParser.createNodesFromPathData(to));
42 | }
43 |
44 | /**
45 | * Align two VectorDrawable sequences in order to make them morphable
46 | */
47 | public static String[] align(String from, String to, Mode alignMode) {
48 | String result[] = null;
49 |
50 | //read data as nodes
51 | PathParser.PathDataNode[] fromList = PathParser.createNodesFromPathData(from);
52 | PathParser.PathDataNode[] toList = PathParser.createNodesFromPathData(to);
53 |
54 | System.out.println("Sequences sizes: " + fromList.length + " / " + toList.length);
55 |
56 | if (PathParser.canMorph(fromList, toList)) {
57 | result = new String[] { from, to };
58 | System.out.println(" >> Paths are already morphable!!! Leaving sequences untouched <<");
59 | } else {
60 |
61 | //Aligning
62 | boolean equivalent = false;
63 | int extraCloneNodes = 0;
64 | ArrayList alignedFrom = null;
65 | ArrayList alignedTo = null;
66 |
67 | for (int i = 0; i < MAX_ALIGN_ITERATIONS && !equivalent; i++) {
68 | System.out.println(i + ". align iteration...");
69 | NWAlignment nw = new NWAlignment(PathNodeUtils.transform(fromList, extraCloneNodes, true),
70 | PathNodeUtils.transform(toList, extraCloneNodes, true));
71 | nw.align();
72 | alignedFrom = nw.getAlignedFrom();
73 | alignedTo = nw.getAlignedTo();
74 | equivalent = PathNodeUtils.isEquivalent(nw.getOriginalFrom(), nw.getAlignedFrom())
75 | && PathNodeUtils.isEquivalent(nw.getOriginalTo(), nw.getAlignedTo());
76 |
77 | if (equivalent) {
78 | System.out.println("Alignment found!");
79 | System.out.println(PathNodeUtils.pathNodesToString(nw.getAlignedFrom(), true));
80 | System.out.println(PathNodeUtils.pathNodesToString(nw.getAlignedTo(), true));
81 | }
82 | extraCloneNodes++;
83 | }
84 |
85 | if (!equivalent) {
86 | System.err.println("Unable to NW-align lists!");
87 | return null;
88 | } else {
89 | System.out.println("Sequence aligned! (" + alignedFrom.size() + " elements)");
90 | }
91 |
92 | AbstractFillMode fillMode = null;
93 | switch (alignMode) {
94 | case BASE:
95 | fillMode = new BaseFillMode();
96 | break;
97 |
98 | //TODO handle more cases here
99 | }
100 |
101 | //Fill
102 | fillMode.fillInjectedNodes(alignedFrom, alignedTo);
103 |
104 | //Simplify
105 | PathNodeUtils.simplify(alignedFrom, alignedTo);
106 |
107 | result = new String[] {
108 | PathNodeUtils.pathNodesToString(alignedFrom), PathNodeUtils.pathNodesToString(alignedTo)
109 | };
110 | }
111 | return result;
112 | }
113 |
114 | // ###### DEBUG ######
115 | // ###################
116 |
117 | static void printPathData(PathParser.PathDataNode[] data) {
118 | int i = 1;
119 | for (PathParser.PathDataNode node : data) {
120 | System.out.println((i++) + ".\t" + node.type + " " + Arrays.toString(node.params));
121 | }
122 | }
123 |
124 | static void printPathData(ArrayList data) {
125 | int i = 1;
126 | for (PathParser.PathDataNode node : data) {
127 | System.out.println((i++) + ".\t" + node.type + " " + Arrays.toString(node.params));
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/util/techniques/NWAlignment.java:
--------------------------------------------------------------------------------
1 | package im.ene.lab.io_timer.util.techniques;
2 |
3 | import im.ene.lab.io_timer.util.PathNodeUtils;
4 | import im.ene.lab.io_timer.util.PathParser;
5 | import java.util.ArrayList;
6 | import java.util.Iterator;
7 |
8 | /**
9 | * Needleman–Wunsch algorithm re-interpreted to align VectorDrawable sequences.
10 | */
11 | public class NWAlignment {
12 | private ArrayList mListA;
13 | private ArrayList mListB;
14 | private ArrayList mOriginalListA;
15 | private ArrayList mOriginalListB;
16 | private ArrayList mAlignedListA;
17 | private ArrayList mAlignedListB;
18 | private int[][] mD;
19 |
20 | private int match = 1;
21 | private int missmatch = -1;
22 | private int indel = 0;
23 |
24 | public NWAlignment(ArrayList from,
25 | ArrayList to) {
26 | mOriginalListA = from;
27 | mOriginalListB = to;
28 |
29 | mListA = new ArrayList<>();
30 | mListA.addAll(mOriginalListA);
31 |
32 | mListB = new ArrayList<>();
33 | mListB.addAll(mOriginalListB);
34 |
35 | //Add dumb command at start
36 | PathParser.PathDataNode dumbNode = new PathParser.PathDataNode('D', new float[] {});
37 | mListA.add(0, dumbNode);
38 | mListB.add(0, dumbNode);
39 | }
40 |
41 | private void initMatrixD() {
42 | mD = new int[mListA.size() + 1][mListB.size() + 1];
43 | for (int i = 0; i <= mListA.size(); i++) {
44 | for (int j = 0; j <= mListB.size(); j++) {
45 | if (i == 0) {
46 | mD[i][j] = -j;
47 | } else if (j == 0) {
48 | mD[i][j] = -i;
49 | } else {
50 | mD[i][j] = 0;
51 | }
52 | }
53 | }
54 | }
55 |
56 | public void align() {
57 | initMatrixD();
58 | mAlignedListA = new ArrayList<>();
59 | mAlignedListB = new ArrayList<>();
60 |
61 | //process matrix D
62 | for (int i = 1; i <= mListA.size(); i++) {
63 | for (int j = 1; j <= mListB.size(); j++) {
64 | int scoreDiag = mD[i - 1][j - 1] + getScore(i, j);
65 | int scoreLeft = mD[i][j - 1] + indel;
66 | int scoreUp = mD[i - 1][j] + indel;
67 | mD[i][j] = Math.max(Math.max(scoreDiag, scoreLeft), scoreUp);
68 | }
69 | }
70 |
71 | //backtracking
72 | int i = mListA.size();
73 | int j = mListB.size();
74 | float[] arrayType = new float[] {};
75 | while (i > 0 && j > 0) {
76 |
77 | PathParser.PathDataNode newNodeA = null;
78 | PathParser.PathDataNode newNodeB = null;
79 | if (mD[i][j] == mD[i - 1][j - 1] + getScore(i, j)) {
80 | newNodeA = mListA.get(i - 1);
81 | newNodeB = mListB.get(j - 1);
82 | i--;
83 | j--;
84 | } else if (mD[i][j] == mD[i][j - 1] + indel) {
85 | newNodeA = new PathParser.PathDataNode(PathNodeUtils.CMD_PLACEHOLDER, arrayType);
86 | newNodeB = mListB.get(j - 1);
87 | j--;
88 | } else {
89 | newNodeA = mListA.get(i - 1);
90 | newNodeB = new PathParser.PathDataNode(PathNodeUtils.CMD_PLACEHOLDER, arrayType);
91 | i--;
92 | }
93 |
94 | //insert new nodes in reverse order
95 | mAlignedListA.add(0, newNodeA);
96 | mAlignedListB.add(0, newNodeB);
97 | }
98 |
99 | //Remove dumbs node
100 | Iterator iterators[] = new Iterator[] { mAlignedListA.iterator(), mAlignedListB.iterator() };
101 | for (Iterator it : iterators) {
102 | while (it.hasNext()) {
103 | PathParser.PathDataNode node = (PathParser.PathDataNode) it.next();
104 | if (node.type == PathNodeUtils.CMD_DUMB) it.remove();
105 | }
106 | }
107 | }
108 |
109 | private int getScore(int i, int j) {
110 | if (mListA.get(i - 1).type == mListB.get(j - 1).type) {
111 | return match; //Arbitrary positive score
112 | } else {
113 | return missmatch; //Arbitrary negative score
114 | }
115 | }
116 |
117 | public ArrayList getAlignedFrom() {
118 | return mAlignedListA;
119 | }
120 |
121 | public ArrayList getAlignedTo() {
122 | return mAlignedListB;
123 | }
124 |
125 | public ArrayList getOriginalFrom() {
126 | return mOriginalListA;
127 | }
128 |
129 | public ArrayList getOriginalTo() {
130 | return mOriginalListB;
131 | }
132 |
133 | void printMatrix() {
134 | System.out.println("D =");
135 | for (int i = 0; i < mListA.size() + 1; i++) {
136 | for (int j = 0; j < mListB.size() + 1; j++) {
137 | System.out.print(String.format("%4d ", mD[i][j]));
138 | }
139 | System.out.println();
140 | }
141 | System.out.println();
142 | }
143 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/io_1_drawable_ic_hash_io16.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
24 |
25 |
31 |
32 |
37 |
38 |
43 |
44 |
49 |
50 |
53 |
60 |
61 |
68 |
69 |
76 |
77 |
84 |
85 |
86 |
93 |
94 |
101 |
102 |
109 |
110 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/util/techniques/BaseFillMode.java:
--------------------------------------------------------------------------------
1 | package im.ene.lab.io_timer.util.techniques;
2 |
3 | import im.ene.lab.io_timer.util.PathNodeUtils;
4 | import im.ene.lab.io_timer.util.PathParser;
5 | import java.util.ArrayList;
6 | import java.util.Arrays;
7 |
8 | /**
9 | * Created by ziby on 07/08/15.
10 | */
11 | public class BaseFillMode extends AbstractFillMode {
12 |
13 | @Override public void fillInjectedNodes(ArrayList from,
14 | ArrayList to) {
15 | PathParser.PathDataNode nodePlaceholder, nodeMaster;
16 |
17 | float[][] penPosFrom = PathNodeUtils.calculatePenPosition(from);
18 | float[][] penPosTo = PathNodeUtils.calculatePenPosition(to);
19 | float[][] penPosPlaceholder = null;
20 | float[][] penPosMaster = null;
21 | ArrayList listMaster = null;
22 | ArrayList listPlaceholder = null;
23 |
24 | for (int i = 0; i < from.size(); i++) {
25 | if (from.get(i).type == PathNodeUtils.CMD_PLACEHOLDER) {
26 | nodePlaceholder = from.get(i);
27 | nodeMaster = to.get(i);
28 | penPosPlaceholder = penPosFrom;
29 | penPosMaster = penPosTo;
30 | listMaster = to;
31 | listPlaceholder = from;
32 | } else if (to.get(i).type == PathNodeUtils.CMD_PLACEHOLDER) {
33 | nodePlaceholder = to.get(i);
34 | nodeMaster = from.get(i);
35 | penPosPlaceholder = penPosTo;
36 | penPosMaster = penPosFrom;
37 | listMaster = from;
38 | listPlaceholder = to;
39 | } else {
40 | nodeMaster = null;
41 | nodePlaceholder = null;
42 | }
43 |
44 | if (nodeMaster == null || nodePlaceholder == null) continue;
45 |
46 | nodePlaceholder.type = nodeMaster.type;
47 | nodePlaceholder.params =
48 | PathParser.copyOfRange(nodeMaster.params, 0, nodeMaster.params.length);
49 |
50 | float lastPlaceholderX, lastPlaceholderY, lastMasterX, lastMasterY;
51 | if (i > 0) {
52 | lastPlaceholderX = penPosPlaceholder[i - 1][0];
53 | lastPlaceholderY = penPosPlaceholder[i - 1][1];
54 | lastMasterX = penPosMaster[i - 1][0];
55 | lastMasterY = penPosMaster[i - 1][1];
56 | } else {
57 | lastPlaceholderX = 0;
58 | lastPlaceholderY = 0;
59 | lastMasterX = 0;
60 | lastMasterY = 0;
61 | }
62 |
63 | if (Character.toLowerCase(nodeMaster.type) == 'z') {
64 | //Injecting a 'z' means we need to counterbalance the last path position with an extra 'm'
65 | PathParser.PathDataNode extraMoveNodePlaceholder =
66 | new PathParser.PathDataNode('M', new float[] { lastPlaceholderX, lastPlaceholderY });
67 | PathParser.PathDataNode extraMoveNodeMaster =
68 | new PathParser.PathDataNode('M', new float[] { lastMasterX, lastMasterY });
69 |
70 | listMaster.add(i, extraMoveNodeMaster);
71 | listPlaceholder.add(i, extraMoveNodePlaceholder);
72 | i++; //next item is already filled, we just added it
73 |
74 | //Recalculate penpos arrays //TODO do this more efficiently
75 | penPosFrom = PathNodeUtils.calculatePenPosition(from);
76 | penPosTo = PathNodeUtils.calculatePenPosition(to);
77 | } else if (Character.isLowerCase(
78 | nodePlaceholder.type)) { // this is a relative movement. If we want to create extra nodes, we need to create neutral relative commands
79 | Arrays.fill(nodePlaceholder.params, 0.0f); //FIXME is good?
80 | } else {
81 | if (nodePlaceholder.type == 'V') {
82 | nodePlaceholder.params[0] = lastPlaceholderY;
83 | } else if (nodePlaceholder.type == 'H') {
84 | nodePlaceholder.params[0] = lastPlaceholderX;
85 | } else {
86 | for (int j = 0; j < nodePlaceholder.params.length; j++) {
87 | nodePlaceholder.params[j++] = lastPlaceholderX;
88 | nodePlaceholder.params[j] = lastPlaceholderY;
89 | }
90 | }
91 | }
92 | }
93 |
94 | float[][] penPosFromAfter = PathNodeUtils.calculatePenPosition(from);
95 | float[][] penPosToAfter = PathNodeUtils.calculatePenPosition(to);
96 |
97 | if ((penPosFromAfter[penPosFromAfter.length - 1][0] == penPosFrom[penPosFrom.length - 1][0])
98 | && (penPosFromAfter[penPosFromAfter.length - 1][1] == penPosFrom[penPosFrom.length - 1][1])
99 | && (penPosToAfter[penPosToAfter.length - 1][0] == penPosTo[penPosTo.length - 1][0])
100 | && (penPosToAfter[penPosToAfter.length - 1][1] == penPosTo[penPosTo.length - 1][1])) {
101 |
102 | System.out.println("Injection completed correctly!");
103 | } else {
104 | System.out.println("PROBLEM during injection!");
105 | System.out.println("PenPos from");
106 | StringBuffer sb = new StringBuffer();
107 | int i = 0;
108 | for (float[] coord : penPosFrom) {
109 | sb.append((++i) + "p. " + coord[0] + " , " + coord[1] + "\n");
110 | }
111 | System.out.println(sb.toString());
112 |
113 | System.out.println("PenPos fromAfter");
114 | sb = new StringBuffer();
115 | i = 0;
116 | for (float[] coord : penPosFromAfter) {
117 | sb.append((++i) + "p. " + coord[0] + " , " + coord[1] + "\n");
118 | }
119 | System.out.println(sb.toString());
120 |
121 | System.out.println("PenPos to");
122 | sb = new StringBuffer();
123 | i = 0;
124 | for (float[] coord : penPosTo) {
125 | sb.append((++i) + "p. " + coord[0] + " , " + coord[1] + "\n");
126 | }
127 | System.out.println(sb.toString());
128 |
129 | System.out.println("PenPos toAfter");
130 | sb = new StringBuffer();
131 | i = 0;
132 | for (float[] coord : penPosToAfter) {
133 | sb.append((++i) + "p. " + coord[0] + " , " + coord[1] + "\n");
134 | }
135 | System.out.println(sb.toString());
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
25 |
33 |
34 |
42 |
43 |
54 |
55 |
61 |
62 |
76 |
77 |
83 |
84 |
85 |
86 |
93 |
94 |
110 |
111 |
117 |
118 |
119 |
120 |
121 |
129 |
130 |
131 |
137 |
138 |
139 |
147 |
148 |
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/ui/widget/GoogleIO2016NumberView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 eneim@Eneim Labs, nam@ene.im
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package im.ene.lab.io_timer.ui.widget;
18 |
19 | import android.animation.Animator;
20 | import android.animation.AnimatorListenerAdapter;
21 | import android.animation.PropertyValuesHolder;
22 | import android.animation.TypeEvaluator;
23 | import android.animation.ValueAnimator;
24 | import android.content.Context;
25 | import android.content.res.TypedArray;
26 | import android.graphics.Canvas;
27 | import android.support.v7.widget.AppCompatImageButton;
28 | import android.util.AttributeSet;
29 | import android.util.Pair;
30 | import android.util.TypedValue;
31 | import android.view.InflateException;
32 | import im.ene.lab.io_timer.R;
33 | import im.ene.lab.io_timer.util.AnimationUtil;
34 | import im.ene.lab.io_timer.util.Number;
35 | import im.ene.lab.io_timer.util.NumberDrawer;
36 | import im.ene.lab.io_timer.util.PathParser;
37 |
38 | /**
39 | * Created by eneim on 3/4/16.
40 | */
41 | public class GoogleIO2016NumberView extends AppCompatImageButton {
42 |
43 | private static final int NO_VALUE = -1;
44 |
45 | private int mValue;
46 |
47 | private Number mNumber;
48 |
49 | private PathParser.PathDataNode[] mNodes;
50 |
51 | private NumberDrawer.NumberDrawable mDrawable;
52 |
53 | private int mSize;
54 |
55 | public GoogleIO2016NumberView(Context context) {
56 | this(context, null);
57 | }
58 |
59 | public GoogleIO2016NumberView(Context context, AttributeSet attrs) {
60 | this(context, attrs, 0);
61 | }
62 |
63 | public GoogleIO2016NumberView(Context context, AttributeSet attrs, int defStyleAttr) {
64 | super(context, attrs, defStyleAttr);
65 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GoogleIO2016NumberView);
66 | mValue = a.getInt(R.styleable.GoogleIO2016NumberView_value, -1);
67 |
68 | TypedValue typedValue = new TypedValue();
69 | context.getTheme().resolveAttribute(R.attr.actionBarSize, typedValue, true);
70 | mSize = context.getResources().getDimensionPixelSize(typedValue.resourceId);
71 |
72 | if (a.hasValue(R.styleable.GoogleIO2016NumberView_size)) {
73 | mSize = a.getDimensionPixelSize(R.styleable.GoogleIO2016NumberView_size, mSize);
74 | }
75 | a.recycle();
76 |
77 | if (mValue == NO_VALUE) {
78 | throw new IllegalArgumentException("Value must be set");
79 | }
80 |
81 | mNumber = Number.VALUES[mValue];
82 | mNodes = Number.getNodes(mNumber);
83 | mDrawable = (NumberDrawer.NumberDrawable) NumberDrawer.getDrawable(mNumber, mSize);
84 | // setImageDrawable(mDrawable);
85 | }
86 |
87 | private PathParser.PathDataNode[] nodesFrom;
88 | private PathParser.PathDataNode[] nodesTo;
89 | private TypeEvaluator evaluator;
90 | private ValueAnimator animator;
91 |
92 | /**
93 | * Run animation to transform current value to next value
94 | *
95 | * @param number : value to transform to.
96 | */
97 | public void animateTo(final Number number) {
98 | if (number == mNumber) {
99 | return;
100 | }
101 |
102 | // obtain new aligned pair of path value
103 | Pair pathValues = Number.alignNumbers(new Pair<>(mNumber, number));
104 | nodesFrom = PathParser.createNodesFromPathData(pathValues.first);
105 | nodesTo = PathParser.createNodesFromPathData(pathValues.second);
106 |
107 | PropertyValuesHolder valuesHolder;
108 | evaluator = new AnimationUtil.PathDataEvaluator(PathParser.deepCopyNodes(nodesFrom));
109 | if (!PathParser.canMorph(nodesFrom, nodesTo)) {
110 | throw new InflateException(" Can't morph from " + pathValues.first + " to " +
111 | pathValues.second);
112 | }
113 |
114 | valuesHolder = PropertyValuesHolder.ofObject("pathData", evaluator, nodesFrom, nodesTo);
115 | animator = ValueAnimator.ofPropertyValuesHolder(valuesHolder);
116 | if (animator != null) {
117 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
118 | @Override public void onAnimationUpdate(ValueAnimator animation) {
119 | mNodes = (PathParser.PathDataNode[]) animation.getAnimatedValue();
120 | invalidate();
121 | }
122 | });
123 | animator.addListener(new AnimatorListenerAdapter() {
124 | @Override public void onAnimationEnd(Animator animation) {
125 | mNumber = number;
126 | reset();
127 | }
128 | });
129 | animator.start();
130 | }
131 | }
132 |
133 | private static final String TAG = "GoogleIo2016NumberView";
134 |
135 | public void reset() {
136 | mNodes = Number.getNodes(mNumber);
137 | mDrawable = (NumberDrawer.NumberDrawable) NumberDrawer.getDrawable(mNumber, mSize);
138 | invalidate();
139 | }
140 |
141 | public void setNumber(Number number) {
142 | if (this.mNumber != number) {
143 | mNumber = number;
144 | reset();
145 | }
146 | }
147 |
148 | @Override protected void onDraw(Canvas canvas) {
149 | super.onDraw(canvas);
150 | int width = getWidth() - getPaddingLeft() - getPaddingRight();
151 | int height = getHeight() - getPaddingTop() - getPaddingBottom();
152 | if (width < 0) {
153 | width = mSize;
154 | }
155 |
156 | if (height < 0) {
157 | height = mSize;
158 | }
159 |
160 | NumberDrawer.draw(canvas, width, height, getPaddingLeft(), getPaddingTop(), mNodes);
161 | }
162 |
163 | @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
164 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
165 | setMeasuredDimension(mSize + getPaddingLeft() + getPaddingRight(),
166 | mSize + getPaddingTop() + getPaddingBottom());
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/util/NumberDrawer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 eneim@Eneim Labs, nam@ene.im
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package im.ene.lab.io_timer.util;
18 |
19 | import android.graphics.Canvas;
20 | import android.graphics.Color;
21 | import android.graphics.ColorFilter;
22 | import android.graphics.Matrix;
23 | import android.graphics.Paint;
24 | import android.graphics.Path;
25 | import android.graphics.PorterDuff;
26 | import android.graphics.PorterDuffColorFilter;
27 | import android.graphics.Rect;
28 | import android.graphics.drawable.Drawable;
29 | import android.support.annotation.ColorInt;
30 | import java.lang.*;
31 |
32 | /**
33 | * Created by eneim on 3/4/16.
34 | */
35 | public final class NumberDrawer {
36 |
37 | private static final Paint p = new Paint();
38 | private static final Paint ps = new Paint();
39 | private static final Path t = new Path();
40 | private static final Matrix m = new Matrix();
41 | private static float od;
42 | protected static ColorFilter cf = null;
43 |
44 | private static @ColorInt int strokeColor = Color.parseColor("#78909C");
45 |
46 | /**
47 | * IMPORTANT: Due to the static usage of this class this
48 | * method sets the tint color statically. So it is highly
49 | * recommended to call the clearColorTint method when you
50 | * have finished drawing.
51 | *
52 | * Sets the color to use when drawing the SVG. This replaces
53 | * all parts of the drawable which are not completely
54 | * transparent with this color.
55 | */
56 | public static void setColorTint(int color) {
57 | cf = new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN);
58 | }
59 |
60 | public static void clearColorTint(int color) {
61 | cf = null;
62 | }
63 |
64 | public static void draw(Canvas c, int w, int h, int dx, int dy, PathParser.PathDataNode[] nodes) {
65 | float ow = 132f;
66 | float oh = 132f;
67 |
68 | od = (w / ow < h / oh) ? w / ow : h / oh;
69 |
70 | r();
71 | c.save();
72 | c.translate((w - od * ow) / 2f + dx, (h - od * oh) / 2f + dy);
73 |
74 | m.reset();
75 | m.setScale(od, od);
76 |
77 | c.save();
78 | ps.setColor(Color.argb(0, 0, 0, 0));
79 | ps.setStrokeCap(Paint.Cap.BUTT);
80 | ps.setStrokeJoin(Paint.Join.MITER);
81 | ps.setStrokeMiter(4.0f * od);
82 | c.scale(1.0f, 1.0f);
83 | c.save();
84 | p.setColor(Color.argb(0, 0, 0, 0));
85 | ps.setColor(strokeColor);
86 | ps.setStrokeWidth(5.0f * od);
87 | ps.setStrokeJoin(Paint.Join.ROUND);
88 | t.reset();
89 |
90 | for (PathParser.PathDataNode node : nodes) {
91 | if (node.type == 'M') {
92 | t.moveTo(node.params[0], node.params[1]);
93 | } else if (node.type == 'L') {
94 | t.lineTo(node.params[0], node.params[1]);
95 | } else if (node.type == 'C') {
96 | t.cubicTo(node.params[0], node.params[1], node.params[2], node.params[3], node.params[4],
97 | node.params[5]);
98 | }
99 | }
100 |
101 | t.transform(m);
102 | c.drawPath(t, p);
103 | c.drawPath(t, ps);
104 | c.restore();
105 | r(3, 2, 0, 1);
106 | p.setColor(Color.argb(0, 0, 0, 0));
107 | ps.setColor(Color.parseColor("#e35444"));
108 | ps.setStrokeWidth(5.0f * od);
109 | ps.setStrokeJoin(Paint.Join.ROUND);
110 | c.restore();
111 | r();
112 |
113 | c.restore();
114 | }
115 |
116 | public static Drawable getDrawable(Number number, int size) {
117 | return new NumberDrawable(number, size);
118 | }
119 |
120 | public static Drawable getTintedDrawable(Number number, int size, int color) {
121 | return new NumberDrawable(number, size, color);
122 | }
123 |
124 | public static class NumberDrawable extends Drawable {
125 | private int size = 0;
126 | private ColorFilter colorFilter = null;
127 |
128 | private Number number;
129 |
130 | public NumberDrawable(Number number, int size) {
131 | this.number = number;
132 | this.size = size;
133 | setBounds(0, 0, size, size);
134 | invalidateSelf();
135 | }
136 |
137 | public NumberDrawable(Number number, int size, int color) {
138 | this(number, size);
139 | colorFilter = new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN);
140 | }
141 |
142 | @Override public int getIntrinsicHeight() {
143 | return size;
144 | }
145 |
146 | @Override public int getIntrinsicWidth() {
147 | return size;
148 | }
149 |
150 | @Override public void draw(Canvas c) {
151 | Rect b = getBounds();
152 | NumberDrawer.cf = colorFilter;
153 | NumberDrawer.draw(c, b.width(), b.height(), b.left, b.top, Number.getNodes(number));
154 | NumberDrawer.cf = null;
155 | }
156 |
157 | @Override public void setAlpha(int i) {
158 | }
159 |
160 | @Override public void setColorFilter(ColorFilter c) {
161 | colorFilter = c;
162 | invalidateSelf();
163 | }
164 |
165 | @Override public int getOpacity() {
166 | return 0;
167 | }
168 | }
169 |
170 | private static void r(Integer... o) {
171 | p.reset();
172 | ps.reset();
173 | if (cf != null) {
174 | p.setColorFilter(cf);
175 | ps.setColorFilter(cf);
176 | }
177 | p.setAntiAlias(true);
178 | ps.setAntiAlias(true);
179 | p.setStyle(Paint.Style.FILL);
180 | ps.setStyle(Paint.Style.STROKE);
181 | for (Integer i : o) {
182 | switch (i) {
183 | case 0:
184 | ps.setStrokeJoin(Paint.Join.MITER);
185 | break;
186 | case 1:
187 | ps.setStrokeMiter(4.0f * od);
188 | break;
189 | case 2:
190 | ps.setStrokeCap(Paint.Cap.BUTT);
191 | break;
192 | case 3:
193 | ps.setColor(Color.argb(0, 0, 0, 0));
194 | break;
195 | }
196 | }
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/ui/widget/ForegroundLinearLayout.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package im.ene.lab.io_timer.ui.widget;
18 |
19 | import android.content.Context;
20 | import android.content.res.TypedArray;
21 | import android.graphics.Canvas;
22 | import android.graphics.Rect;
23 | import android.graphics.drawable.Drawable;
24 | import android.os.Build;
25 | import android.support.annotation.NonNull;
26 | import android.support.v7.widget.LinearLayoutCompat;
27 | import android.util.AttributeSet;
28 | import android.view.Gravity;
29 | import im.ene.lab.io_timer.R;
30 |
31 | /**
32 | * @hide
33 | */
34 | public class ForegroundLinearLayout extends LinearLayoutCompat {
35 |
36 | private Drawable mForeground;
37 |
38 | private final Rect mSelfBounds = new Rect();
39 |
40 | private final Rect mOverlayBounds = new Rect();
41 |
42 | private int mForegroundGravity = Gravity.FILL;
43 |
44 | protected boolean mForegroundInPadding = true;
45 |
46 | boolean mForegroundBoundsChanged = false;
47 |
48 | public ForegroundLinearLayout(Context context) {
49 | this(context, null);
50 | }
51 |
52 | public ForegroundLinearLayout(Context context, AttributeSet attrs) {
53 | this(context, attrs, 0);
54 | }
55 |
56 | public ForegroundLinearLayout(Context context, AttributeSet attrs, int defStyle) {
57 | super(context, attrs, defStyle);
58 |
59 | TypedArray a =
60 | context.obtainStyledAttributes(attrs, R.styleable.ForegroundLinearLayout, defStyle, 0);
61 |
62 | mForegroundGravity =
63 | a.getInt(R.styleable.ForegroundLinearLayout_android_foregroundGravity, mForegroundGravity);
64 |
65 | final Drawable d = a.getDrawable(R.styleable.ForegroundLinearLayout_android_foreground);
66 | if (d != null) {
67 | setForeground(d);
68 | }
69 |
70 | mForegroundInPadding =
71 | a.getBoolean(R.styleable.ForegroundLinearLayout_foregroundInsidePadding, true);
72 |
73 | a.recycle();
74 | }
75 |
76 | /**
77 | * Describes how the foreground is positioned.
78 | *
79 | * @return foreground gravity.
80 | * @see #setForegroundGravity(int)
81 | */
82 | public int getForegroundGravity() {
83 | return mForegroundGravity;
84 | }
85 |
86 | /**
87 | * Describes how the foreground is positioned. Defaults to START and TOP.
88 | *
89 | * @param foregroundGravity See {@link android.view.Gravity}
90 | * @see #getForegroundGravity()
91 | */
92 | public void setForegroundGravity(int foregroundGravity) {
93 | if (mForegroundGravity != foregroundGravity) {
94 | if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
95 | foregroundGravity |= Gravity.START;
96 | }
97 |
98 | if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
99 | foregroundGravity |= Gravity.TOP;
100 | }
101 |
102 | mForegroundGravity = foregroundGravity;
103 |
104 | if (mForegroundGravity == Gravity.FILL && mForeground != null) {
105 | Rect padding = new Rect();
106 | mForeground.getPadding(padding);
107 | }
108 |
109 | requestLayout();
110 | }
111 | }
112 |
113 | @Override protected boolean verifyDrawable(Drawable who) {
114 | return super.verifyDrawable(who) || (who == mForeground);
115 | }
116 |
117 | @Override public void jumpDrawablesToCurrentState() {
118 | super.jumpDrawablesToCurrentState();
119 | if (mForeground != null) {
120 | mForeground.jumpToCurrentState();
121 | }
122 | }
123 |
124 | @Override protected void drawableStateChanged() {
125 | super.drawableStateChanged();
126 | if (mForeground != null && mForeground.isStateful()) {
127 | mForeground.setState(getDrawableState());
128 | }
129 | }
130 |
131 | /**
132 | * Supply a Drawable that is to be rendered on top of all of the child
133 | * views in the frame layout. Any padding in the Drawable will be taken
134 | * into account by ensuring that the children are inset to be placed
135 | * inside of the padding area.
136 | *
137 | * @param drawable The Drawable to be drawn on top of the children.
138 | */
139 | public void setForeground(Drawable drawable) {
140 | if (mForeground != drawable) {
141 | if (mForeground != null) {
142 | mForeground.setCallback(null);
143 | unscheduleDrawable(mForeground);
144 | }
145 |
146 | mForeground = drawable;
147 |
148 | if (drawable != null) {
149 | setWillNotDraw(false);
150 | drawable.setCallback(this);
151 | if (drawable.isStateful()) {
152 | drawable.setState(getDrawableState());
153 | }
154 | if (mForegroundGravity == Gravity.FILL) {
155 | Rect padding = new Rect();
156 | drawable.getPadding(padding);
157 | }
158 | } else {
159 | setWillNotDraw(true);
160 | }
161 | requestLayout();
162 | invalidate();
163 | }
164 | }
165 |
166 | /**
167 | * Returns the drawable used as the foreground of this FrameLayout. The
168 | * foreground drawable, if non-null, is always drawn on top of the children.
169 | *
170 | * @return A Drawable or null if no foreground was set.
171 | */
172 | public Drawable getForeground() {
173 | return mForeground;
174 | }
175 |
176 | @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
177 | super.onLayout(changed, left, top, right, bottom);
178 | mForegroundBoundsChanged |= changed;
179 | }
180 |
181 | @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
182 | super.onSizeChanged(w, h, oldw, oldh);
183 | mForegroundBoundsChanged = true;
184 | }
185 |
186 | @Override public void draw(@NonNull Canvas canvas) {
187 | super.draw(canvas);
188 |
189 | if (mForeground != null) {
190 | final Drawable foreground = mForeground;
191 |
192 | if (mForegroundBoundsChanged) {
193 | mForegroundBoundsChanged = false;
194 | final Rect selfBounds = mSelfBounds;
195 | final Rect overlayBounds = mOverlayBounds;
196 |
197 | final int w = getRight() - getLeft();
198 | final int h = getBottom() - getTop();
199 |
200 | if (mForegroundInPadding) {
201 | selfBounds.set(0, 0, w, h);
202 | } else {
203 | selfBounds.set(getPaddingLeft(), getPaddingTop(), w - getPaddingRight(),
204 | h - getPaddingBottom());
205 | }
206 |
207 | Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
208 | foreground.getIntrinsicHeight(), selfBounds, overlayBounds);
209 | foreground.setBounds(overlayBounds);
210 | }
211 |
212 | foreground.draw(canvas);
213 | }
214 | }
215 |
216 | @Override public void drawableHotspotChanged(float x, float y) {
217 | super.drawableHotspotChanged(x, y);
218 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
219 | if (mForeground != null) {
220 | mForeground.setHotspot(x, y);
221 | }
222 | }
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/util/PathDataSource.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 eneim@Eneim Labs, nam@ene.im
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package im.ene.lab.io_timer.util;
18 |
19 | /**
20 | * Created by eneim on 3/4/16.
21 | */
22 | final class PathDataSource {
23 |
24 | static final String SRC_ZERO = "M66,130 C101.227,129.927 129.906,101.221 130,66 "
25 | + "C129.915,30.778 101.223,2.084 66,2 C30.829,2.143 2.085,30.815 2,66 "
26 | + "C2.106,101.182 30.821,129.882 66,130 L66,130 Z";
27 |
28 | static final String SRC_ONE = "M50,2 L80.813,2 L80.813,130 L50,130 L50,2 Z";
29 |
30 | static final String SRC_TWO =
31 | "M108.405,130.00025 L41.001,130.00025 C41.001,130.00025 50.923,121.62325 56.738,116.75025 "
32 | + "C63.061,111.45125 70.487,105.29925 88.19,87.5072496 "
33 | + "C92.017,83.6612496 95.074,80.2632496 97.519,77.1732496 "
34 | + "C106.07,66.2152496 107.203,58.7102496 107.492,49.1482496 "
35 | + "C107.884,36.2052496 94.156,19.1432496 70.997,20.8092496 "
36 | + "C47.838,22.4752496 41.001,44.1302496 41.001,44.1302496 L23,25.4352496 "
37 | + "C23,25.4352496 29.837,3.77924956 52.996,2.11324956 "
38 | + "C76.155,0.447249557 89.884,17.5092496 89.492,30.4532496 "
39 | + "C89.117,42.8092496 87.892,51.0202496 70.189,68.8122496 "
40 | + "C67.6,71.4142496 65.215,73.7782496 63.007,75.9352496 "
41 | + "C50.439,88.1872496 43.254,94.1232496 37.925,98.5882496 "
42 | + "C32.109,103.46125 23,111.30525 23,111.30525 L91.071,111.30525 "
43 | + "L108.405,130.00025 L108.405,130.00025 Z";
44 |
45 | static final String SRC_THREE =
46 | "M39.564,103.813841 C39.564,103.813841 42.569,113.480841 50.837,121.521841 "
47 | + "C59.785,130.223841 78.949,133.532841 92.833,125.147841 "
48 | + "C106.053,117.162841 111.102,100.370841 107.965,89.248841 "
49 | + "C103.759,74.335841 91.315,72.002841 91.315,72.002841 "
50 | + "C91.315,72.002841 92.96,70.883841 94.142,69.998841 "
51 | + "C98.03,66.857841 104.552,60.038841 104.552,49.632841 "
52 | + "C104.552,34.806841 93.604,27.072841 89.304,24.481841 "
53 | + "C85.004,21.890841 76.109,18.566841 62.175,22.279841 "
54 | + "C48.243,25.991841 41.695,42.322841 41.695,42.322841 "
55 | + "L24.13,23.779841 C24.13,23.779841 29.895,8.18284098 44.611,3.73684098 "
56 | + "C58.414,-0.434159025 68.298,3.88784098 72.598,6.47884098 "
57 | + "C76.898,9.06984098 87.352,17.082841 87.444,31.890841 "
58 | + "C87.537,46.715841 73.75,53.459841 73.75,53.459841 "
59 | + "C73.75,53.459841 78.273,56.147841 82.682,60.313841 "
60 | + "C86.38,64.100841 90.965,70.154841 90.965,75.162841 "
61 | + "C90.965,86.634841 87.699,98.234841 75.225,106.459841 "
62 | + "C61.977,115.196841 41.427,111.134841 33.272,102.978841 "
63 | + "C25.117,94.823841 22,85.270841 22,85.270841 L39.564,103.813841 L39.564,103.813841 Z";
64 |
65 | static final String SRC_FOUR =
66 | "M115.548,107.092 L35.208,107.092 L35.208,95.956 L85.64,21.185 L101.548,21.185 L101.548,130 "
67 | + "L83.34,110.816 L83.34,2 L67.431,2 L17,76.772 L17,87.908 L59.192,87.908 "
68 | + "L97.339,87.908 L115.548,107.092 L115.548,107.092 Z";
69 |
70 | static final String SRC_FIVE =
71 | "M20,82.333 L37.854,101.143 C37.854,101.143 41.693,111.223 42.561,112.722 "
72 | + "C43.428,114.22 53.155,130 73.887,130 C94.617,130 110.699,113.738 110.699,94.124 "
73 | + "C110.699,74.51 93.911,58.874 75.297,58.874 C74.484,58.874 71.726,59.104 70.926,59.163 "
74 | + "C54.333,61.326 41.285,74.937 41.285,74.937 L48.148,20.811 L104.147,20.811 L86.294,2 "
75 | + "L30.295,2 L23.433,56.127 C23.433,56.127 37.634,42.727 53.865,40.258 "
76 | + "C55.042,40.132 56.236,40.064 57.443,40.064 C76.058,40.064 92.846,55.7 92.846,75.314 "
77 | + "C92.846,94.927 76.764,111.19 56.033,111.19 C35.303,111.19 25.574,95.41 24.707,93.911 "
78 | + "C23.84,92.413 20,82.333 20,82.333 L20,82.333 Z";
79 |
80 | static final String SRC_SIX =
81 | "M65.5539859,62.744 C65.5539859,62.744 69.6309859,61.961 70.0629859,61.856 L69.6149859,61.92 "
82 | + "C74.0449859,61.286 82.1309859,60.034 91.8249859,64.436 "
83 | + "C106.476986,71.089 110.698986,85.554 110.164986,96.23 "
84 | + "C109.657986,106.389 100.142986,125.209 81.1049859,129.214 "
85 | + "C77.3909859,129.995 73.8059859,130.173 70.3969859,129.842 "
86 | + "C56.7889859,128.119 45.1409859,118.156 40.4789859,106.321 "
87 | + "C34.5499859,91.272 43.3629859,75.207 46.8829859,68.504 "
88 | + "C50.4049859,61.8 79.1519859,20.424 79.1519859,20.424 L68.2259859,8.909 "
89 | + "L61.6709859,2 C61.6709859,2 32.9249859,43.376 29.4039859,50.08 "
90 | + "C25.8819859,56.783 17.0699859,72.848 22.9979859,87.898 "
91 | + "C28.3839859,101.569 43.1609859,112.483 59.8459859,111.385 "
92 | + "C61.0889859,111.255 62.3499859,111.058 63.6249859,110.79 "
93 | + "C82.6629859,106.785 92.1779859,87.965 92.6849859,77.806 "
94 | + "C93.2179859,67.13 88.9959859,52.665 74.3449859,46.012 "
95 | + "C61.8659859,40.345 48.0739859,44.32 48.0739859,44.32 "
96 | + "L65.5539859,62.744 L65.5539859,62.744 Z";
97 |
98 | static final String SRC_SEVEN =
99 | "M56.855,130 L108.642,35.473 L108.642,20.81 L39.853,20.81 L22,2 L58,2 L90.789,2 "
100 | + "L90.789,16.662 L39.001,111.19 L56.855,130 L56.855,130 Z";
101 |
102 | static final String SRC_EIGHT = "M25.701,82.8 C26.544,80.777 28.242,77.209 29.542,75.385 "
103 | + "C33.234,70.201 38.061,66.12 44.022,63.139 L44.022,62.424 "
104 | + "C39.374,59.446 35.558,55.662 32.58,51.072 C29.6,46.486 28.111,41.687 28.111,36.681 "
105 | + "C28.111,29.842 30.73,22.395 34.093,17.122 C35.4,15.288 36.923,13.555 38.659,11.922 "
106 | + "C45.689,5.307 54.628,2 65.474,2 C76.318,2 85.257,5.307 92.291,11.922 "
107 | + "C99.321,18.536 102.838,26.79 102.838,36.681 "
108 | + "C102.838,38.937 102.535,41.15 101.93,43.321 "
109 | + "C101.197,45.394 99.663,49.079 98.369,51.072 "
110 | + "C95.388,55.662 91.575,59.446 86.928,62.424 L86.928,63.139 "
111 | + "C92.885,66.12 97.712,70.201 101.407,75.385 "
112 | + "C105.1,80.569 106.949,86.558 106.949,93.352 "
113 | + "C106.949,101.189 103.43,109.593 98.869,115.524 "
114 | + "C97.697,116.886 96.398,118.196 94.972,119.452 C86.986,126.485 77.153,130 65.474,130 "
115 | + "C53.793,130 43.961,126.485 35.977,119.452 C27.991,112.421 24,103.72 24,93.352 "
116 | + "C24,89.588 24.566,86.07 25.701,82.8 L25.701,82.8 Z";
117 |
118 | static final String SRC_NINE = "M62.9830112,123.091342 L69.5380112,130.000342 "
119 | + "C69.5380112,130.000342 98.2840112,88.6243418 101.805011,81.9203418 "
120 | + "C105.327011,75.2173418 114.139011,59.1523418 108.211011,44.1023418 "
121 | + "C102.825011,30.4313418 88.0470112,19.5173418 71.3630112,20.6153418 "
122 | + "C70.1200112,20.7453418 68.8590112,20.9423418 67.5840112,21.2103418 "
123 | + "C48.5450112,25.2153418 39.0310112,44.0353418 38.5240112,54.1943418 "
124 | + "C37.9910112,64.8703418 42.2120112,79.3353418 56.8640112,85.9883418 "
125 | + "C69.3430112,91.6553418 83.1350112,87.6803418 83.1350112,87.6803418 "
126 | + "L65.6550112,69.2563418 C65.6550112,69.2563418 61.5780112,70.0393418 61.1460112,70.1443418 "
127 | + "L61.5940112,70.0803418 C57.1640112,70.7143418 49.0780112,71.9663418 39.3840112,67.5643418 "
128 | + "C24.7320112,60.9113418 20.5100112,46.4463418 21.0440112,35.7703418 "
129 | + "C21.5500112,25.6113418 31.0660112,6.79134177 50.1040112,2.78634177 "
130 | + "C53.8170112,2.00534177 57.4030112,1.82734177 60.8120112,2.15834177 "
131 | + "C74.4200112,3.88134177 86.0680112,13.8443418 90.7300112,25.6793418 "
132 | + "C96.6590112,40.7283418 87.8460112,56.7933418 84.3260112,63.4973418 "
133 | + "C80.8040112,70.2003418 52.0570112,111.576342 52.0570112,111.576342 "
134 | + "L62.9830112,123.091342 L62.9830112,123.091342 Z";
135 | }
136 |
--------------------------------------------------------------------------------
/app/src/main/res/values/svg_paths.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 | M66,130 C101.227,129.927 129.906,101.221 130,66 C129.915,30.778 101.223,2.084 66,2 C30.829,2.143 2.085,30.815 2,66 C2.106,101.182 30.821,129.882 66,130 L66,130 Z
20 |
21 |
22 |
23 | M50,2 L80.813,2 L80.813,130 L50,130 L50,2 Z
24 |
25 |
26 |
27 | M108.405,130.00025 L41.001,130.00025 C41.001,130.00025 50.923,121.62325 56.738,116.75025 C63.061,111.45125 70.487,105.29925 88.19,87.5072496 C92.017,83.6612496 95.074,80.2632496 97.519,77.1732496 C106.07,66.2152496 107.203,58.7102496 107.492,49.1482496 C107.884,36.2052496 94.156,19.1432496 70.997,20.8092496 C47.838,22.4752496 41.001,44.1302496 41.001,44.1302496 L23,25.4352496 C23,25.4352496 29.837,3.77924956 52.996,2.11324956 C76.155,0.447249557 89.884,17.5092496 89.492,30.4532496 C89.117,42.8092496 87.892,51.0202496 70.189,68.8122496 C67.6,71.4142496 65.215,73.7782496 63.007,75.9352496 C50.439,88.1872496 43.254,94.1232496 37.925,98.5882496 C32.109,103.46125 23,111.30525 23,111.30525 L91.071,111.30525 L108.405,130.00025 L108.405,130.00025 Z
28 |
29 |
30 |
31 | M39.564,103.813841 C39.564,103.813841 42.569,113.480841 50.837,121.521841 C59.785,130.223841 78.949,133.532841 92.833,125.147841 C106.053,117.162841 111.102,100.370841 107.965,89.248841 C103.759,74.335841 91.315,72.002841 91.315,72.002841 C91.315,72.002841 92.96,70.883841 94.142,69.998841 C98.03,66.857841 104.552,60.038841 104.552,49.632841 C104.552,34.806841 93.604,27.072841 89.304,24.481841 C85.004,21.890841 76.109,18.566841 62.175,22.279841 C48.243,25.991841 41.695,42.322841 41.695,42.322841 L24.13,23.779841 C24.13,23.779841 29.895,8.18284098 44.611,3.73684098 C58.414,-0.434159025 68.298,3.88784098 72.598,6.47884098 C76.898,9.06984098 87.352,17.082841 87.444,31.890841 C87.537,46.715841 73.75,53.459841 73.75,53.459841 C73.75,53.459841 78.273,56.147841 82.682,60.313841 C86.38,64.100841 90.965,70.154841 90.965,75.162841 C90.965,86.634841 87.699,98.234841 75.225,106.459841 C61.977,115.196841 41.427,111.134841 33.272,102.978841 C25.117,94.823841 22,85.270841 22,85.270841 L39.564,103.813841 L39.564,103.813841 Z
32 |
33 |
34 |
35 | M115.548,107.092 L35.208,107.092 L35.208,95.956 L85.64,21.185 L101.548,21.185 L101.548,130 L83.34,110.816 L83.34,2 L67.431,2 L17,76.772 L17,87.908 L59.192,87.908 L97.339,87.908 L115.548,107.092 L115.548,107.092 Z
36 |
37 |
38 |
39 | M20,82.333 L37.854,101.143 C37.854,101.143 41.693,111.223 42.561,112.722 C43.428,114.22 53.155,130 73.887,130 C94.617,130 110.699,113.738 110.699,94.124 C110.699,74.51 93.911,58.874 75.297,58.874 C74.484,58.874 71.726,59.104 70.926,59.163 C54.333,61.326 41.285,74.937 41.285,74.937 L48.148,20.811 L104.147,20.811 L86.294,2 L30.295,2 L23.433,56.127 C23.433,56.127 37.634,42.727 53.865,40.258 C55.042,40.132 56.236,40.064 57.443,40.064 C76.058,40.064 92.846,55.7 92.846,75.314 C92.846,94.927 76.764,111.19 56.033,111.19 C35.303,111.19 25.574,95.41 24.707,93.911 C23.84,92.413 20,82.333 20,82.333 L20,82.333 Z
40 |
41 |
42 |
43 | M65.5539859,62.744 C65.5539859,62.744 69.6309859,61.961 70.0629859,61.856 L69.6149859,61.92 C74.0449859,61.286 82.1309859,60.034 91.8249859,64.436 C106.476986,71.089 110.698986,85.554 110.164986,96.23 C109.657986,106.389 100.142986,125.209 81.1049859,129.214 C77.3909859,129.995 73.8059859,130.173 70.3969859,129.842 C56.7889859,128.119 45.1409859,118.156 40.4789859,106.321 C34.5499859,91.272 43.3629859,75.207 46.8829859,68.504 C50.4049859,61.8 79.1519859,20.424 79.1519859,20.424 L68.2259859,8.909 L61.6709859,2 C61.6709859,2 32.9249859,43.376 29.4039859,50.08 C25.8819859,56.783 17.0699859,72.848 22.9979859,87.898 C28.3839859,101.569 43.1609859,112.483 59.8459859,111.385 C61.0889859,111.255 62.3499859,111.058 63.6249859,110.79 C82.6629859,106.785 92.1779859,87.965 92.6849859,77.806 C93.2179859,67.13 88.9959859,52.665 74.3449859,46.012 C61.8659859,40.345 48.0739859,44.32 48.0739859,44.32 L65.5539859,62.744 L65.5539859,62.744 Z
44 |
45 |
46 |
47 | M56.855,130 L108.642,35.473 L108.642,20.81 L39.853,20.81 L22,2 L58,2 L90.789,2 L90.789,16.662 L39.001,111.19 L56.855,130 L56.855,130 Z
48 |
49 |
50 |
51 | M25.701,82.8 C26.544,80.777 28.242,77.209 29.542,75.385 C33.234,70.201 38.061,66.12 44.022,63.139 L44.022,62.424 C39.374,59.446 35.558,55.662 32.58,51.072 C29.6,46.486 28.111,41.687 28.111,36.681 C28.111,29.842 30.73,22.395 34.093,17.122 C35.4,15.288 36.923,13.555 38.659,11.922 C45.689,5.307 54.628,2 65.474,2 C76.318,2 85.257,5.307 92.291,11.922 C99.321,18.536 102.838,26.79 102.838,36.681 C102.838,38.937 102.535,41.15 101.93,43.321 C101.197,45.394 99.663,49.079 98.369,51.072 C95.388,55.662 91.575,59.446 86.928,62.424 L86.928,63.139 C92.885,66.12 97.712,70.201 101.407,75.385 C105.1,80.569 106.949,86.558 106.949,93.352 C106.949,101.189 103.43,109.593 98.869,115.524 C97.697,116.886 96.398,118.196 94.972,119.452 C86.986,126.485 77.153,130 65.474,130 C53.793,130 43.961,126.485 35.977,119.452 C27.991,112.421 24,103.72 24,93.352 C24,89.588 24.566,86.07 25.701,82.8 L25.701,82.8 Z
52 |
53 |
54 |
55 | M62.9830112,123.091342 L69.5380112,130.000342 C69.5380112,130.000342 98.2840112,88.6243418 101.805011,81.9203418 C105.327011,75.2173418 114.139011,59.1523418 108.211011,44.1023418 C102.825011,30.4313418 88.0470112,19.5173418 71.3630112,20.6153418 C70.1200112,20.7453418 68.8590112,20.9423418 67.5840112,21.2103418 C48.5450112,25.2153418 39.0310112,44.0353418 38.5240112,54.1943418 C37.9910112,64.8703418 42.2120112,79.3353418 56.8640112,85.9883418 C69.3430112,91.6553418 83.1350112,87.6803418 83.1350112,87.6803418 L65.6550112,69.2563418 C65.6550112,69.2563418 61.5780112,70.0393418 61.1460112,70.1443418 L61.5940112,70.0803418 C57.1640112,70.7143418 49.0780112,71.9663418 39.3840112,67.5643418 C24.7320112,60.9113418 20.5100112,46.4463418 21.0440112,35.7703418 C21.5500112,25.6113418 31.0660112,6.79134177 50.1040112,2.78634177 C53.8170112,2.00534177 57.4030112,1.82734177 60.8120112,2.15834177 C74.4200112,3.88134177 86.0680112,13.8443418 90.7300112,25.6793418 C96.6590112,40.7283418 87.8460112,56.7933418 84.3260112,63.4973418 C80.8040112,70.2003418 52.0570112,111.576342 52.0570112,111.576342 L62.9830112,123.091342 L62.9830112,123.091342 Z
56 |
57 |
58 |
59 |
60 | M 62.98301,123.09134 L 69.53801,130.00034 C 69.53801,130.00034,98.28401,88.624344,101.80501,81.92034 C 105.32701,75.21734,114.13901,59.15234,108.21101,44.10234 C 102.82501,30.431341,88.04701,19.517342,71.363014,20.615341 C 70.12001,20.745342,68.85901,20.94234,67.584015,21.210342 C 48.54501,25.215342,39.03101,44.035343,38.52401,54.194344 C 37.991013,64.87034,42.21201,79.33534,56.86401,85.98834 C 69.34301,91.65534,83.13501,87.68034,83.13501,87.68034 L 65.655014,69.25634 C 65.655014,69.25634,61.57801,70.039345,61.14601,70.14434 L 61.59401,70.080345 C 57.164013,70.71434,49.07801,71.96634,39.38401,67.56434 L 39.38401,67.56434 L 39.38401,67.56434 L 39.38401,67.56434 C 24.732012,60.911343,20.510012,46.446342,21.044012,35.77034 C 21.55001,25.611341,31.066011,6.791342,50.10401,2.7863417 C 53.817013,2.0053418,57.40301,1.8273418,60.81201,2.158342 C 74.42001,3.8813417,86.06801,13.844342,90.73001,25.679342 C 96.65901,40.72834,87.84601,56.793343,84.32601,63.49734 C 80.80401,70.20034,52.05701,111.57634,52.05701,111.57634 L 62.98301,123.09134
61 |
62 |
63 |
64 | M 20.0,82.333 L 37.854,101.143 C 37.854,101.143,37.854,101.143,37.854,101.143 C 37.854,101.143,41.693,111.223,42.561,112.722 C 43.428,114.22,53.155,130.0,73.887,130.0 C 94.617,130.0,110.699,113.738,110.699,94.124 C 110.699,74.51,93.911,58.874,75.297,58.874 C 74.484,58.874,71.726,59.104,70.926,59.163 C 54.333,61.326,41.285,74.937,41.285,74.937 L 48.148,20.811 C 48.148,20.811,48.148,20.811,48.148,20.811 L 104.147,20.811 C 104.147,20.811,104.147,20.811,104.147,20.811 L 86.294,2.0 L 30.295,2.0 L 23.433,56.127 C 23.433,56.127,37.634,42.727,53.865,40.258 C 55.042,40.132,56.236,40.064,57.443,40.064 C 76.058,40.064,92.846,55.7,92.846,75.314 C 92.846,94.927,76.764,111.19,56.033,111.19 C 35.303,111.19,25.574,95.41,24.707,93.911 C 23.84,92.413,20.0,82.333,20.0,82.333 L 20.0,82.333
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/ui/MainActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 eneim@Eneim Labs, nam@ene.im
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package im.ene.lab.io_timer.ui;
18 |
19 | import android.content.Intent;
20 | import android.graphics.drawable.Animatable;
21 | import android.net.Uri;
22 | import android.os.Bundle;
23 | import android.support.v4.view.ViewCompat;
24 | import android.support.v4.widget.DrawerLayout;
25 | import android.support.v7.app.AlertDialog;
26 | import android.support.v7.app.AppCompatActivity;
27 | import android.support.v7.app.AppCompatDelegate;
28 | import android.support.v7.widget.AppCompatImageView;
29 | import android.view.View;
30 | import android.view.animation.AccelerateInterpolator;
31 | import android.view.animation.Interpolator;
32 | import android.widget.ImageView;
33 | import butterknife.Bind;
34 | import butterknife.BindDimen;
35 | import butterknife.ButterKnife;
36 | import butterknife.OnClick;
37 | import im.ene.lab.io_timer.R;
38 | import im.ene.lab.io_timer.ui.widget.ForegroundLinearLayout;
39 | import im.ene.lab.io_timer.ui.widget.GoogleIO2016NumberView;
40 | import im.ene.lab.io_timer.ui.widget.MiniDrawerLayout;
41 | import im.ene.lab.io_timer.util.Number;
42 | import java.util.Arrays;
43 | import java.util.List;
44 | import java.util.concurrent.TimeUnit;
45 | import org.threeten.bp.ZonedDateTime;
46 | import rx.Observable;
47 | import rx.Subscription;
48 | import rx.android.schedulers.AndroidSchedulers;
49 | import rx.functions.Action1;
50 | import rx.functions.Func1;
51 | import rx.schedulers.Schedulers;
52 |
53 | public class MainActivity extends AppCompatActivity {
54 |
55 | @Bind(R.id.logo) ImageView mLogo;
56 | @Bind(R.id.io_animation) AppCompatImageView mCrafty; // in the name of the creator
57 |
58 | /**
59 | * This is either AnimatedVectorDrawable (on Lollipop and up) or AnimatedVectorDrawableCompat, but
60 | * I just care of it as a Animatable implementation.
61 | */
62 | private Animatable mCraftyDrawable;
63 | @Bind(R.id.side_logo) ImageView mHeader;
64 | @Bind(R.id.drawer) MiniDrawerLayout mDrawer;
65 | @Bind(R.id.extra_container) View mExtraViews;
66 | @Bind(R.id.settings) ForegroundLinearLayout mSetting;
67 |
68 | @OnClick(R.id.side_logo) void openCloseDrawer() {
69 | if (mDrawer != null) {
70 | if (mDrawer.isDrawerOpen()) {
71 | mDrawer.closeDrawer();
72 | } else {
73 | mDrawer.openDrawer();
74 | }
75 | }
76 | }
77 |
78 | @OnClick(R.id.settings) void openWebsite() {
79 | String url = "https://events.google.com/io2016/";
80 | Intent intent = new Intent(Intent.ACTION_VIEW);
81 | intent.setData(Uri.parse(url));
82 | startActivity(intent);
83 | }
84 |
85 | @OnClick(R.id.credits) void openCredits() {
86 | new AlertDialog.Builder(this).setTitle("About resources used in IO When")
87 | .setView(R.layout.credits_container)
88 | .create()
89 | .show();
90 | }
91 |
92 | @Bind({
93 | R.id.digit_0, R.id.digit_1, R.id.digit_2, R.id.digit_3, R.id.digit_4, R.id.digit_5,
94 | R.id.digit_6, R.id.digit_7
95 | }) List mDigitViews;
96 |
97 | @BindDimen(R.dimen.padding_8dp) int sideBarPadding;
98 | @BindDimen(R.dimen.drawer_collapse_size) int collapseSize;
99 | @BindDimen(R.dimen.drawer_expand_size) int expandSize;
100 |
101 | private Subscription mTimeSetupSubscription;
102 | private Integer[] mDigits = new Integer[8];
103 | private long mEventSecond;
104 |
105 | private int iconMaxSize;
106 | private int iconMinSize;
107 |
108 | private final Interpolator interpolator = new AccelerateInterpolator();
109 |
110 | static {
111 | // This is my expected mode in the future, so skip it for now.
112 | // AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
113 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO);
114 | }
115 |
116 | @Override protected void onCreate(Bundle savedInstanceState) {
117 | super.onCreate(savedInstanceState);
118 | setContentView(R.layout.activity_main);
119 | ButterKnife.bind(this);
120 |
121 | // get AnimatedVectorDrawable
122 | mCraftyDrawable = (Animatable) mCrafty.getDrawable();
123 |
124 | // Update date time then update UI.
125 | mEventSecond = ZonedDateTime.parse(getString(R.string.io_event_date)).toEpochSecond();
126 |
127 | iconMinSize = collapseSize - 2 * sideBarPadding;
128 | iconMaxSize = expandSize - 2 * sideBarPadding;
129 |
130 | ViewCompat.setPivotX(mHeader, 0);
131 | ViewCompat.setPivotY(mHeader, 0);
132 |
133 | mDrawerListener = new DrawerLayout.SimpleDrawerListener() {
134 | @Override public void onDrawerSlide(View drawerView, float value) {
135 | float scale = (iconMaxSize * value + iconMinSize * (1.f - value)) / iconMaxSize;
136 | if (mHeader != null) {
137 | ViewCompat.setScaleX(mHeader, scale);
138 | ViewCompat.setScaleY(mHeader, scale);
139 | mHeader.setAlpha(1 - scale);
140 | }
141 |
142 | if (mCrafty != null) {
143 | mCrafty.setAlpha(value);
144 | }
145 |
146 | if (mExtraViews != null) {
147 | mExtraViews.setAlpha(interpolator.getInterpolation(value));
148 | }
149 | }
150 |
151 | @Override public void onDrawerClosed(View drawerView) {
152 | if (mSetting != null) {
153 | mSetting.setClickable(false);
154 | }
155 |
156 | if (mCraftyDrawable != null && !mCraftyDrawable.isRunning()) {
157 | mCraftyDrawable.stop();
158 | }
159 | }
160 |
161 | @Override public void onDrawerOpened(View drawerView) {
162 | if (mSetting != null) {
163 | mSetting.setClickable(true);
164 | }
165 |
166 | if (mCraftyDrawable != null && !mCraftyDrawable.isRunning()) {
167 | mCraftyDrawable.start();
168 | }
169 | }
170 | };
171 |
172 | mDrawer.setDrawerListener(mDrawerListener);
173 | mDrawerListener.onDrawerSlide(mDrawer, 0.f); // bad practice
174 | }
175 |
176 | private static final String TAG = "MainActivity";
177 |
178 | private DrawerLayout.DrawerListener mDrawerListener;
179 |
180 | @Override protected void onResume() {
181 | super.onResume();
182 | mTimeSetupSubscription = Observable.interval(1, TimeUnit.SECONDS)
183 | .map(new Func1() {
184 | @Override public Void call(Long aLong) {
185 | if (mDigits == null) {
186 | mDigits = new Integer[8];
187 | }
188 | // Parse date-time
189 | long now = ZonedDateTime.now().toEpochSecond();
190 | long diff = mEventSecond - now;
191 | if (diff <= 0) {
192 | Arrays.fill(mDigits, 0);
193 | } else {
194 | // diff: second
195 | // diff / 60 minutes
196 | int dayLeft = (int) (diff / (60 * 60 * 24));
197 | mDigits[0] = dayLeft / 10;
198 | mDigits[1] = dayLeft % 10;
199 | diff -= dayLeft * (60 * 60 * 24);
200 | int hours = (int) (diff / (60 * 60));
201 | mDigits[2] = hours / 10;
202 | mDigits[3] = hours % 10;
203 | diff -= hours * (60 * 60);
204 | int minutes = (int) (diff / 60);
205 | mDigits[4] = minutes / 10;
206 | mDigits[5] = minutes % 10;
207 | diff -= minutes * 60;
208 | int seconds = (int) diff;
209 | mDigits[6] = seconds / 10;
210 | mDigits[7] = seconds % 10;
211 | }
212 |
213 | return null;
214 | }
215 | })
216 | .subscribeOn(Schedulers.io())
217 | .observeOn(AndroidSchedulers.mainThread())
218 | .subscribe(new Action1() {
219 | @Override public void call(Void param) {
220 | activeCountDown();
221 | mLogo.setVisibility(View.GONE);
222 | }
223 | });
224 | }
225 |
226 | private void activeCountDown() {
227 | for (int i = 0, count = Math.min(mDigits.length, mDigitViews.size()); i < count; i++) {
228 | mDigitViews.get(i).animateTo(Number.VALUES[mDigits[i]]);
229 | }
230 | }
231 |
232 | @Override protected void onPause() {
233 | if (mTimeSetupSubscription != null && !mTimeSetupSubscription.isUnsubscribed()) {
234 | mTimeSetupSubscription.unsubscribe();
235 | }
236 | super.onPause();
237 | }
238 |
239 | @Override protected void onDestroy() {
240 | mDrawer.setDrawerListener(null);
241 | mDrawerListener = null;
242 | super.onDestroy();
243 | }
244 | }
245 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
26 |
27 |
33 |
34 |
41 |
42 |
47 |
48 |
57 |
58 |
66 |
67 |
75 |
76 |
77 |
87 |
88 |
89 |
94 |
95 |
104 |
105 |
113 |
114 |
122 |
123 |
124 |
134 |
135 |
136 |
137 |
144 |
145 |
150 |
151 |
160 |
161 |
169 |
170 |
178 |
179 |
180 |
190 |
191 |
192 |
197 |
198 |
207 |
208 |
216 |
217 |
225 |
226 |
227 |
237 |
238 |
239 |
240 |
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/util/PathNodeUtils.java:
--------------------------------------------------------------------------------
1 | package im.ene.lab.io_timer.util;
2 |
3 | import java.text.DecimalFormat;
4 | import java.util.ArrayList;
5 | import java.util.Arrays;
6 | import java.util.Iterator;
7 |
8 | /**
9 | * Created by ziby on 07/08/15.
10 | */
11 | public class PathNodeUtils {
12 |
13 | public static final char CMD_PLACEHOLDER = '#';
14 | public static final char CMD_DUMB = 'D';
15 |
16 | /**
17 | * Return the number of arguments expected for a specific command
18 | */
19 | static int commandArguments(char type) {
20 | switch (type) {
21 | case CMD_PLACEHOLDER:
22 | case 'z':
23 | case 'Z':
24 | return 0;
25 | case 'm':
26 | case 'M':
27 | case 'l':
28 | case 'L':
29 | case 't':
30 | case 'T':
31 | return 2;
32 | case 'h':
33 | case 'H':
34 | case 'v':
35 | case 'V':
36 | return 1;
37 | case 'c':
38 | case 'C':
39 | return 6;
40 | case 's':
41 | case 'S':
42 | case 'q':
43 | case 'Q':
44 | return 4;
45 | case 'a':
46 | case 'A':
47 | return 7;
48 | }
49 | return -1;
50 | }
51 |
52 | /**
53 | * Check if the given node sequences are equivalent (in terms of incremental graphic commands).
54 | * //FIXME this is an approximated method
55 | */
56 | public static boolean isEquivalent(ArrayList original,
57 | ArrayList alternative) {
58 | int innerStart = 0;
59 | for (PathParser.PathDataNode o : original) {
60 | boolean found = false;
61 | for (int i = innerStart; i < alternative.size() && !found; i++) {
62 | PathParser.PathDataNode n = alternative.get(i);
63 | if ((o.type == n.type && Arrays.equals(o.params, n.params)) || ((o.type == 'Z'
64 | || o.type == 'z') && n.type == 'L' /*transformZ*/)) {
65 | found = true;
66 | innerStart = i + 1;
67 | }
68 | }
69 |
70 | if (!found) return false;
71 | }
72 |
73 | return true;
74 | }
75 |
76 | public static ArrayList transform(PathParser.PathDataNode[] elements) {
77 | return transform(elements, 0, true);
78 | }
79 |
80 | /**
81 | * Transform the input list. Expand (eventually) compressed element and create extra copy if
82 | * needed.
83 | */
84 | public static ArrayList transform(PathParser.PathDataNode[] elements,
85 | int extraCopy, boolean transformZ) {
86 | if (elements == null) return null;
87 |
88 | ArrayList transformed = new ArrayList<>();
89 | for (PathParser.PathDataNode node : elements) {
90 |
91 | int cmdArgs = commandArguments(node.type);
92 | int argsProvided = node.params.length;
93 |
94 | if (node.type == 'z') node.type = 'Z';
95 |
96 | if (cmdArgs == -1) {
97 | System.err.println("Command not supported! " + node.type);
98 | } else if (argsProvided < cmdArgs) {
99 | System.err.println("Command "
100 | + node.type
101 | + " requires "
102 | + cmdArgs
103 | + " params! Passing only "
104 | + argsProvided);
105 | } else if (cmdArgs == node.params.length) {
106 | //Normal command with the exact number of params
107 | transformed.add(node);
108 | if (extraCopy > 0
109 | && (transformZ || node.type != 'Z')
110 | && node.type != PathNodeUtils.CMD_DUMB) { //never add extra z/Z or dumb commands
111 | PathParser.PathDataNode extraNodes = new PathParser.PathDataNode(node);
112 | if (Character.isLowerCase(
113 | node.type)) { // this is a relative movement. If we want to create extra nodes, we need to create neutral relative commands
114 | Arrays.fill(extraNodes.params, 0.0f); //FIXME is good?
115 | }
116 |
117 | for (int j = 0; j < extraCopy; j++)
118 | transformed.add(extraNodes);
119 | }
120 | } else {
121 | //Multiple groups of params, verify consistency
122 | int mod = (argsProvided % cmdArgs);
123 | if (mod != 0) {
124 | System.err.println("Providing multiple groups of params for command "
125 | + node.type
126 | + ", but in wrong number (missing "
127 | + mod
128 | + " args)");
129 | } else {
130 | //Create multiple nodes
131 | int iter = argsProvided / cmdArgs;
132 | for (int i = 0; i < iter; i++) {
133 | PathParser.PathDataNode newNode = new PathParser.PathDataNode(node.type,
134 | PathParser.copyOfRange(node.params, i * cmdArgs, (i + 1) * cmdArgs));
135 | transformed.add(newNode);
136 |
137 | if (extraCopy > 0) {
138 | PathParser.PathDataNode extraNodes = new PathParser.PathDataNode(newNode);
139 | ;
140 | if (Character.isLowerCase(
141 | newNode.type)) { // this is a relative movement. If we want to create extra nodes, we need to create neutral relative commands
142 | Arrays.fill(extraNodes.params, 0.0f); //FIXME is good?
143 | }
144 |
145 | for (int j = 0; j < extraCopy; j++)
146 | transformed.add(extraNodes);
147 | }
148 | }
149 | }
150 | }
151 | }
152 |
153 | if (transformZ) {
154 | float[][] penPos = PathNodeUtils.calculatePenPosition(transformed);
155 | int i = 0;
156 | for (PathParser.PathDataNode node : transformed) {
157 | if (node.type == 'Z') {
158 | node.type = 'L';
159 | node.params = penPos[i];
160 | }
161 | i++;
162 | }
163 | }
164 |
165 | return transformed;
166 | }
167 |
168 | /**
169 | * Simplify eventually useless nodes
170 | */
171 | static void simplify(ArrayList from,
172 | ArrayList to) {
173 | if (from.size() != to.size()) {
174 | System.err.println("Cannot simplify lists of nodes of different sizes");
175 | return;
176 | }
177 |
178 | System.out.println("Simplify lists with size " + from.size());
179 |
180 | boolean removeIndexes[] = new boolean[from.size()];
181 | int last = from.size() - 1; //avoid last
182 |
183 | for (int i = 0; i < last; i++) {
184 | if (from.get(i).isEqual(from.get(i + 1)) && to.get(i).isEqual(to.get(i + 1))) {
185 | removeIndexes[i] = true;
186 | }
187 | }
188 |
189 | Iterator iterators[] = new Iterator[] { from.iterator(), to.iterator() };
190 | for (Iterator it : iterators) {
191 | int i = 0;
192 | while (it.hasNext()) {
193 | it.next();
194 | if (removeIndexes[i++]) it.remove();
195 | }
196 | }
197 |
198 | System.out.println("Final size after simplify is " + from.size());
199 | }
200 |
201 | public static float[][] calculatePenPosition(ArrayList sequence) {
202 | float penPos[][] = new float[sequence.size()][2];
203 |
204 | float[] lastStart = new float[] { 0, 0 };
205 | float currentX = 0;
206 | float currentY = 0;
207 | boolean saveNewStart = false;
208 |
209 | for (int i = 0; i < sequence.size(); i++) {
210 | PathParser.PathDataNode node = sequence.get(i);
211 | if (node.type == 'z' || node.type == 'Z') { //Close path and restart from last start
212 | currentX = lastStart[0];
213 | currentY = lastStart[1];
214 | saveNewStart = true;
215 | } else {
216 | float[] positionFromParams = getPositionFromParams(node);
217 | if (positionFromParams != null) {
218 | if (Character.isLowerCase(
219 | node.type)) { //relative movement (it's already correct in case of 'v' or 'h'
220 | currentX += positionFromParams[0];
221 | currentY += positionFromParams[1];
222 | } else { //absolute movement
223 | if (i > 0 && node.type == 'V') {
224 | currentX = penPos[i - 1][0];
225 | currentY = positionFromParams[1];
226 | } else if (i > 0 && node.type == 'H') {
227 | currentX = positionFromParams[0];
228 | currentY = penPos[i - 1][1];
229 | } else {
230 | currentX = positionFromParams[0];
231 | currentY = positionFromParams[1];
232 | }
233 | }
234 |
235 | if (node.type == 'm' || node.type == 'M') {
236 | saveNewStart = true;
237 | }
238 |
239 | if (saveNewStart) {
240 | lastStart = new float[] { currentX, currentY };
241 | saveNewStart = false;
242 | }
243 | }
244 | }
245 |
246 | penPos[i][0] = currentX;
247 | penPos[i][1] = currentY;
248 | }
249 |
250 | return penPos;
251 | }
252 |
253 | static float[] getPositionFromParams(PathParser.PathDataNode node) {
254 | if (node == null || node.params == null || node.params.length == 0) return null;
255 |
256 | float[] ris = new float[2];
257 |
258 | if (Character.toLowerCase(node.type) == 'v') {
259 | ris[1] = node.params[0];
260 | } else if (Character.toLowerCase(node.type) == 'h') {
261 | ris[0] = node.params[0];
262 | } else {
263 | ris[0] = node.params[node.params.length - 2];
264 | ris[1] = node.params[node.params.length - 1];
265 | }
266 | return ris;
267 | }
268 |
269 | /**
270 | * Create a VectorDrawable sequence from a list of nodes
271 | */
272 | public static String pathNodesToString(ArrayList nodes,
273 | boolean onlyCommands) {
274 |
275 | //Format float to avoid scientific notation
276 | DecimalFormat floatFormatter = new DecimalFormat("###.#########");
277 |
278 | StringBuilder sb = new StringBuilder();
279 | for (PathParser.PathDataNode n : nodes) {
280 | sb.append(n.type);
281 | sb.append(' ');
282 | if (!onlyCommands) {
283 | for (float p : n.params) {
284 | if (("" + p).contains("e") || ("" + p).contains("E")) //apply decimal format only for scientific notation number
285 | {
286 | sb.append(floatFormatter.format(p));
287 | } else {
288 | sb.append(p);
289 | }
290 |
291 | sb.append(',');
292 | }
293 | sb.replace(sb.length() - 1, sb.length(), " ");
294 | }
295 | }
296 | return sb.toString();
297 | }
298 |
299 | public static String pathNodesToString(ArrayList nodes) {
300 | return pathNodesToString(nodes, false);
301 | }
302 | }
303 |
--------------------------------------------------------------------------------
/app/src/main/java/im/ene/lab/io_timer/util/PathParser.java:
--------------------------------------------------------------------------------
1 | package im.ene.lab.io_timer.util;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 |
6 | // This class is a duplicate from the PathParser.java of frameworks/base, with slight
7 | // update on incompatible API like copyOfRange().
8 | public class PathParser {
9 | private static final String LOGTAG = "PathParser";
10 |
11 | // Copy from Arrays.copyOfRange() which is only available from API level 9.
12 |
13 | /**
14 | * Copies elements from {@code original} into a new array, from indexes start (inclusive) to
15 | * end (exclusive). The original order of elements is preserved.
16 | * If {@code end} is greater than {@code original.length}, the result is padded
17 | * with the value {@code 0.0f}.
18 | *
19 | * @param original the original array
20 | * @param start the start index, inclusive
21 | * @param end the end index, exclusive
22 | * @return the new array
23 | * @throws ArrayIndexOutOfBoundsException if {@code start < 0 || start > original.length}
24 | * @throws IllegalArgumentException if {@code start > end}
25 | * @throws NullPointerException if {@code original == null}
26 | */
27 | public static float[] copyOfRange(float[] original, int start, int end) {
28 | if (start > end) {
29 | throw new IllegalArgumentException();
30 | }
31 | int originalLength = original.length;
32 | if (start < 0 || start > originalLength) {
33 | throw new ArrayIndexOutOfBoundsException();
34 | }
35 | int resultLength = end - start;
36 | int copyLength = Math.min(resultLength, originalLength - start);
37 | float[] result = new float[resultLength];
38 | System.arraycopy(original, start, result, 0, copyLength);
39 | return result;
40 | }
41 |
42 | /**
43 | * @param pathData The string representing a path, the same as "d" string in svg file.
44 | * @return an array of the PathDataNode.
45 | */
46 | public static PathDataNode[] createNodesFromPathData(String pathData) {
47 | if (pathData == null) {
48 | return null;
49 | }
50 | int start = 0;
51 | int end = 1;
52 |
53 | ArrayList list = new ArrayList();
54 | while (end < pathData.length()) {
55 | end = nextStart(pathData, end);
56 | String s = pathData.substring(start, end).trim();
57 | if (s.length() > 0) {
58 | float[] val = getFloats(s);
59 | addNode(list, s.charAt(0), val);
60 | }
61 |
62 | start = end;
63 | end++;
64 | }
65 | if ((end - start) == 1 && start < pathData.length()) {
66 | addNode(list, pathData.charAt(start), new float[0]);
67 | }
68 | return list.toArray(new PathDataNode[list.size()]);
69 | }
70 |
71 | /**
72 | * @param source The array of PathDataNode to be duplicated.
73 | * @return a deep copy of the source.
74 | */
75 | public static PathDataNode[] deepCopyNodes(PathDataNode[] source) {
76 | if (source == null) {
77 | return null;
78 | }
79 | PathDataNode[] copy = new PathDataNode[source.length];
80 | for (int i = 0; i < source.length; i++) {
81 | copy[i] = new PathDataNode(source[i]);
82 | }
83 | return copy;
84 | }
85 |
86 | /**
87 | * @param nodesFrom The source path represented in an array of PathDataNode
88 | * @param nodesTo The target path represented in an array of PathDataNode
89 | * @return whether the nodesFrom can morph into nodesTo
90 | */
91 | public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) {
92 | if (nodesFrom == null || nodesTo == null) {
93 | return false;
94 | }
95 |
96 | if (nodesFrom.length != nodesTo.length) {
97 | return false;
98 | }
99 |
100 | for (int i = 0; i < nodesFrom.length; i++) {
101 | if (nodesFrom[i].type != nodesTo[i].type
102 | || nodesFrom[i].params.length != nodesTo[i].params.length) {
103 | return false;
104 | }
105 | }
106 | return true;
107 | }
108 |
109 | /**
110 | * Update the target's data to match the source.
111 | * Before calling this, make sure canMorph(target, source) is true.
112 | *
113 | * @param target The target path represented in an array of PathDataNode
114 | * @param source The source path represented in an array of PathDataNode
115 | */
116 | public static void updateNodes(PathDataNode[] target, PathDataNode[] source) {
117 | for (int i = 0; i < source.length; i++) {
118 | target[i].type = source[i].type;
119 | for (int j = 0; j < source[i].params.length; j++) {
120 | target[i].params[j] = source[i].params[j];
121 | }
122 | }
123 | }
124 |
125 | static int nextStart(String s, int end) {
126 | char c;
127 |
128 | while (end < s.length()) {
129 | c = s.charAt(end);
130 | // Note that 'e' or 'E' are not valid path commands, but could be
131 | // used for floating point numbers' scientific notation.
132 | // Therefore, when searching for next command, we should ignore 'e'
133 | // and 'E'.
134 | if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0)) && c != 'e' && c != 'E') {
135 | return end;
136 | }
137 | end++;
138 | }
139 | return end;
140 | }
141 |
142 | static void addNode(ArrayList list, char cmd, float[] val) {
143 | list.add(new PathDataNode(cmd, val));
144 | }
145 |
146 | static class ExtractFloatResult {
147 | // We need to return the position of the next separator and whether the
148 | // next float starts with a '-' or a '.'.
149 | int mEndPosition;
150 | boolean mEndWithNegOrDot;
151 | }
152 |
153 | /**
154 | * Parse the floats in the string.
155 | * This is an optimized version of parseFloat(s.split(",|\\s"));
156 | *
157 | * @param s the string containing a command and list of floats
158 | * @return array of floats
159 | */
160 | static float[] getFloats(String s) {
161 | if (s.charAt(0) == 'z' | s.charAt(0) == 'Z') {
162 | return new float[0];
163 | }
164 | try {
165 | float[] results = new float[s.length()];
166 | int count = 0;
167 | int startPosition = 1;
168 | int endPosition = 0;
169 |
170 | ExtractFloatResult result = new ExtractFloatResult();
171 | int totalLength = s.length();
172 |
173 | // The startPosition should always be the first character of the
174 | // current number, and endPosition is the character after the current
175 | // number.
176 | while (startPosition < totalLength) {
177 | extract(s, startPosition, result);
178 | endPosition = result.mEndPosition;
179 |
180 | if (startPosition < endPosition) {
181 | results[count++] = Float.parseFloat(s.substring(startPosition, endPosition));
182 | }
183 |
184 | if (result.mEndWithNegOrDot) {
185 | // Keep the '-' or '.' sign with next number.
186 | startPosition = endPosition;
187 | } else {
188 | startPosition = endPosition + 1;
189 | }
190 | }
191 | return copyOfRange(results, 0, count);
192 | } catch (NumberFormatException e) {
193 | throw new RuntimeException("error in parsing \"" + s + "\"", e);
194 | }
195 | }
196 |
197 | /**
198 | * Calculate the position of the next comma or space or negative sign
199 | *
200 | * @param s the string to search
201 | * @param start the position to start searching
202 | * @param result the result of the extraction, including the position of the
203 | * the starting position of next number, whether it is ending with a '-'.
204 | */
205 | static void extract(String s, int start, ExtractFloatResult result) {
206 | // Now looking for ' ', ',', '.' or '-' from the start.
207 | int currentIndex = start;
208 | boolean foundSeparator = false;
209 | result.mEndWithNegOrDot = false;
210 | boolean secondDot = false;
211 | boolean isExponential = false;
212 | for (; currentIndex < s.length(); currentIndex++) {
213 | boolean isPrevExponential = isExponential;
214 | isExponential = false;
215 | char currentChar = s.charAt(currentIndex);
216 | switch (currentChar) {
217 | case ' ':
218 | case ',':
219 | foundSeparator = true;
220 | break;
221 | case '-':
222 | // The negative sign following a 'e' or 'E' is not a separator.
223 | if (currentIndex != start && !isPrevExponential) {
224 | foundSeparator = true;
225 | result.mEndWithNegOrDot = true;
226 | }
227 | break;
228 | case '.':
229 | if (!secondDot) {
230 | secondDot = true;
231 | } else {
232 | // This is the second dot, and it is considered as a separator.
233 | foundSeparator = true;
234 | result.mEndWithNegOrDot = true;
235 | }
236 | break;
237 | case 'e':
238 | case 'E':
239 | isExponential = true;
240 | break;
241 | }
242 | if (foundSeparator) {
243 | break;
244 | }
245 | }
246 | // When there is nothing found, then we put the end position to the end
247 | // of the string.
248 | result.mEndPosition = currentIndex;
249 | }
250 |
251 | /**
252 | * Each PathDataNode represents one command in the "d" attribute of the svg
253 | * file.
254 | * An array of PathDataNode can represent the whole "d" attribute.
255 | */
256 | public static class PathDataNode {
257 | public char type;
258 | public float[] params;
259 |
260 | public PathDataNode(char type, float[] params) {
261 | this.type = type;
262 | this.params = params;
263 | }
264 |
265 | public PathDataNode(PathDataNode n) {
266 | type = n.type;
267 | params = copyOfRange(n.params, 0, n.params.length);
268 | }
269 |
270 | public boolean isEqual(PathDataNode otherNode) {
271 | if (otherNode != null && type == otherNode.type && Arrays.equals(params, otherNode.params)) {
272 | return true;
273 | }
274 |
275 | return false;
276 | }
277 |
278 | /**
279 | * The current PathDataNode will be interpolated between the
280 | * nodeFrom and nodeTo according to the
281 | * fraction.
282 | *
283 | * @param nodeFrom The start value as a PathDataNode.
284 | * @param nodeTo The end value as a PathDataNode
285 | * @param fraction The fraction to interpolate.
286 | */
287 | public void interpolatePathDataNode(PathDataNode nodeFrom, PathDataNode nodeTo,
288 | float fraction) {
289 | for (int i = 0; i < nodeFrom.params.length; i++) {
290 | params[i] = nodeFrom.params[i] * (1 - fraction) + nodeTo.params[i] * fraction;
291 | }
292 | }
293 | }
294 |
295 | /*
296 | -----
297 | Android-related code has been removed
298 | */
299 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/colors_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | #fde0dc
20 | #f9bdbb
21 | #f69988
22 | #f36c60
23 | #e84e40
24 | #e51c23
25 | #dd191d
26 | #d01716
27 | #c41411
28 | #b0120a
29 | #ff7997
30 | #ff5177
31 | #ff2d6f
32 | #e00032
33 |
34 | #fce4ec
35 | #f8bbd0
36 | #f48fb1
37 | #f06292
38 | #ec407a
39 | #e91e63
40 | #d81b60
41 | #c2185b
42 | #ad1457
43 | #880e4f
44 | #ff80ab
45 | #ff4081
46 | #f50057
47 | #c51162
48 |
49 | #f3e5f5
50 | #e1bee7
51 | #ce93d8
52 | #ba68c8
53 | #ab47bc
54 | #9c27b0
55 | #8e24aa
56 | #7b1fa2
57 | #6a1b9a
58 | #4a148c
59 | #ea80fc
60 | #e040fb
61 | #d500f9
62 | #aa00ff
63 |
64 | #ede7f6
65 | #d1c4e9
66 | #b39ddb
67 | #9575cd
68 | #7e57c2
69 | #673ab7
70 | #5e35b1
71 | #512da8
72 | #4527a0
73 | #311b92
74 | #b388ff
75 | #7c4dff
76 | #651fff
77 | #6200ea
78 |
79 | #e8eaf6
80 | #c5cae9
81 | #9fa8da
82 | #7986cb
83 | #5c6bc0
84 | #3f51b5
85 | #3949ab
86 | #303f9f
87 | #283593
88 | #1a237e
89 | #8c9eff
90 | #536dfe
91 | #3d5afe
92 | #304ffe
93 |
94 | #e7e9fd
95 | #d0d9ff
96 | #afbfff
97 | #91a7ff
98 | #738ffe
99 | #5677fc
100 | #4e6cef
101 | #455ede
102 | #3b50ce
103 | #2a36b1
104 | #a6baff
105 | #6889ff
106 | #4d73ff
107 | #4d69ff
108 |
109 | #e1f5fe
110 | #b3e5fc
111 | #81d4fa
112 | #4fc3f7
113 | #29b6f6
114 | #03a9f4
115 | #039be5
116 | #0288d1
117 | #0277bd
118 | #01579b
119 | #80d8ff
120 | #40c4ff
121 | #00b0ff
122 | #0091ea
123 |
124 | #e0f7fa
125 | #b2ebf2
126 | #80deea
127 | #4dd0e1
128 | #26c6da
129 | #00bcd4
130 | #00acc1
131 | #0097a7
132 | #00838f
133 | #006064
134 | #84ffff
135 | #18ffff
136 | #00e5ff
137 | #00b8d4
138 |
139 | #e0f2f1
140 | #b2dfdb
141 | #80cbc4
142 | #4db6ac
143 | #26a69a
144 | #009688
145 | #00897b
146 | #00796b
147 | #00695c
148 | #004d40
149 | #a7ffeb
150 | #64ffda
151 | #1de9b6
152 | #00bfa5
153 |
154 | #d0f8ce
155 | #a3e9a4
156 | #72d572
157 | #42bd41
158 | #2baf2b
159 | #259b24
160 | #0a8f08
161 | #0a7e07
162 | #056f00
163 | #0d5302
164 | #a2f78d
165 | #5af158
166 | #14e715
167 | #12c700
168 |
169 | #f1f8e9
170 | #dcedc8
171 | #c5e1a5
172 | #aed581
173 | #9ccc65
174 | #8bc34a
175 | #7cb342
176 | #689f38
177 | #558b2f
178 | #33691e
179 | #ccff90
180 | #b2ff59
181 | #76ff03
182 | #64dd17
183 |
184 | #f9fbe7
185 | #f0f4c3
186 | #e6ee9c
187 | #dce775
188 | #d4e157
189 | #cddc39
190 | #c0ca33
191 | #afb42b
192 | #9e9d24
193 | #827717
194 | #f4ff81
195 | #eeff41
196 | #c6ff00
197 | #aeea00
198 |
199 | #fffde7
200 | #fff9c4
201 | #fff59d
202 | #fff176
203 | #ffee58
204 | #ffeb3b
205 | #fdd835
206 | #fbc02d
207 | #f9a825
208 | #f57f17
209 | #ffff8d
210 | #ffff00
211 | #ffea00
212 | #ffd600
213 |
214 | #fff8e1
215 | #ffecb3
216 | #ffe082
217 | #ffd54f
218 | #ffca28
219 | #ffc107
220 | #ffb300
221 | #ffa000
222 | #ff8f00
223 | #ff6f00
224 | #ffe57f
225 | #ffd740
226 | #ffc400
227 | #ffab00
228 |
229 | #fff3e0
230 | #ffe0b2
231 | #ffcc80
232 | #ffb74d
233 | #ffa726
234 | #ff9800
235 | #fb8c00
236 | #f57c00
237 | #ef6c00
238 | #e65100
239 | #ffd180
240 | #ffab40
241 | #ff9100
242 | #ff6d00
243 |
244 | #fbe9e7
245 | #ffccbc
246 | #ffab91
247 | #ff8a65
248 | #ff7043
249 | #ff5722
250 | #f4511e
251 | #e64a19
252 | #d84315
253 | #bf360c
254 | #ff9e80
255 | #ff6e40
256 | #ff3d00
257 | #dd2c00
258 |
259 | #efebe9
260 | #d7ccc8
261 | #bcaaa4
262 | #a1887f
263 | #8d6e63
264 | #795548
265 | #6d4c41
266 | #5d4037
267 | #4e342e
268 | #3e2723
269 |
270 | #fafafa
271 | #f5f5f5
272 | #eeeeee
273 | #e0e0e0
274 | #bdbdbd
275 | #9e9e9e
276 | #757575
277 | #616161
278 | #424242
279 | #212121
280 | #000000
281 | #ffffff
282 |
283 | #eceff1
284 | #cfd8dc
285 | #b0bec5
286 | #90a4ae
287 | #78909c
288 | #607d8b
289 | #546e7a
290 | #455a64
291 | #37474f
292 | #263238
293 |
--------------------------------------------------------------------------------
/.idea/codeStyleSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
326 |
327 |
328 |
--------------------------------------------------------------------------------