├── 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 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/eneim.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | [![Beerpay](https://beerpay.io/eneim/Google_io_2016_timer/badge.svg?style=beer-square)](https://beerpay.io/eneim/Google_io_2016_timer) [![Beerpay](https://beerpay.io/eneim/Google_io_2016_timer/make-wish.svg?style=flat-square)](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 |

226 | 227 | 228 | class 229 | 230 | 231 |
232 |
233 | 234 | 235 | layout 236 | 237 | 238 |
239 |
240 | 241 | 242 | xmlns:android 243 | 244 | 245 |
246 |
247 | 248 | 249 | xmlns:.* 250 | 251 | BY_NAME 252 | 253 |
254 |
255 | 256 | 257 | 258 | .*:id 259 | http://schemas.android.com/apk/res/android 260 | 261 | 262 | 263 |
264 |
265 | 266 | 267 | 268 | .*:layout_width 269 | http://schemas.android.com/apk/res/android 270 | 271 | 272 | 273 |
274 |
275 | 276 | 277 | 278 | .*:layout_height 279 | http://schemas.android.com/apk/res/android 280 | 281 | 282 | 283 |
284 |
285 | 286 | 287 | 288 | .*:layout_.* 289 | http://schemas.android.com/apk/res/android 290 | 291 | 292 | BY_NAME 293 | 294 |
295 |
296 | 297 | 298 | 299 | .* 300 | http://schemas.android.com/apk/res/android 301 | 302 | 303 | BY_NAME 304 | 305 |
306 |
307 | 308 | 309 | .*(?<!style)$ 310 | 311 | BY_NAME 312 | 313 |
314 |
315 | 316 | 317 | style 318 | 319 | 320 |
321 | 322 | 323 | 324 | 325 | 326 |