abouttreesummaryrefslogcommitdiff
diff options
context:
space:
mode:
authorJules Aguillon2024-11-18 00:13:08 +0100
committerJules Aguillon2024-11-18 00:13:08 +0100
commit997b7be4c005696df91dc1833b4fb4db6c984991 (patch)
tree768e9cd16851ad12404a8b7c39d197e35932c3ca
parent23097921cfb4e6c86c947ff39438e943bda24f18 (diff)
downloadunexpected-keyboard-997b7be4c005696df91dc1833b4fb4db6c984991.tar.gz
unexpected-keyboard-997b7be4c005696df91dc1833b4fb4db6c984991.zip
launcher: Animated vector describing swipe gesture
Replace the short video with an animated vector image that shows the swipe gesture. This is much lighter and reliable than the mp4 video, which failed to play on many devices. Source for the image of the key is in inkscape SVG format in srcs/res and is converted to an android drawable when needed. The swipe animation is hand-written.
-rw-r--r--build.gradle2
-rw-r--r--res/drawable/doc_anim_swipe.xml25
-rw-r--r--res/drawable/doc_key_u.xml25
-rw-r--r--res/layout/launcher_activity.xml2
-rw-r--r--res/raw/intro_video.mp4bin123771 -> 0 bytes
-rw-r--r--res/values/styles.xml7
-rw-r--r--srcs/juloo.keyboard2/LauncherActivity.java61
-rw-r--r--srcs/res/SvgToVector.java32
-rw-r--r--srcs/res/doc_key.svg192
-rw-r--r--srcs/res/gen_doc_key_drawables.sh25
10 files changed, 340 insertions, 31 deletions
diff --git a/build.gradle b/build.gradle
index 0cb6160..686f84f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -21,7 +21,7 @@ android {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = ['srcs']
+ java.srcDirs = ['srcs/juloo.keyboard2']
res.srcDirs = ['res', 'build/generated-resources']
assets.srcDirs = ['assets']
}
diff --git a/res/drawable/doc_anim_swipe.xml b/res/drawable/doc_anim_swipe.xml
new file mode 100644
index 0000000..6e91d30
--- /dev/null
+++ b/res/drawable/doc_anim_swipe.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:name="vector" android:width="35dp" android:height="40dp" android:viewportWidth="35" android:viewportHeight="40">
+ <path android:name="trace" android:pathData="M 0 0" android:fillColor="#09b3f1" android:strokeColor="#09b3f1" android:strokeWidth="2" android:strokeLineCap="round"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="trace">
+ <aapt:attr name="android:animation">
+ <set>
+ <objectAnimator android:propertyName="fillAlpha" android:duration="100" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" android:interpolator="@android:interpolator/fast_out_slow_in"/>
+ <objectAnimator android:propertyName="strokeAlpha" android:duration="100" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" android:interpolator="@android:interpolator/fast_out_slow_in"/>
+ <objectAnimator
+ android:propertyName="pathData"
+ android:duration="700"
+ android:valueFrom="M 17.5 20.0 m 2,0 a 2,2 0 1,1 -4,0 a 2,2 0 1,1 4,0 M 17.5 20.0 L 17.5,20.0"
+ android:valueTo=" M 31 4 m 2,0 a 2,2 0 1,1 -4,0 a 2,2 0 1,1 4,0 M 17.5 20.0 L 31 ,4"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/linear_out_slow_in"/>
+ <objectAnimator android:propertyName="fillAlpha" android:startOffset="900" android:duration="400" android:valueFrom="1" android:valueTo="0" android:valueType="floatType" android:interpolator="@android:interpolator/linear_out_slow_in"/>
+ <objectAnimator android:propertyName="strokeAlpha" android:startOffset="900" android:duration="400" android:valueFrom="1" android:valueTo="0" android:valueType="floatType" android:interpolator="@android:interpolator/linear_out_slow_in"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/res/drawable/doc_key_u.xml b/res/drawable/doc_key_u.xml
new file mode 100644
index 0000000..4a94fe2
--- /dev/null
+++ b/res/drawable/doc_key_u.xml
@@ -0,0 +1,25 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="35dp"
+ android:height="40dp"
+ android:viewportWidth="35.0"
+ android:viewportHeight="40.0">
+ <path
+ android:pathData="M11.26,10.13L23.74,10.13A1.13,1.13 0,0 1,24.87 11.26L24.87,28.74A1.13,1.13 0,0 1,23.74 29.88L11.26,29.88A1.13,1.13 0,0 1,10.13 28.74L10.13,11.26A1.13,1.13 0,0 1,11.26 10.13z"
+ android:fillAlpha="1"
+ android:strokeColor="#404040"
+ android:fillColor="#333333"
+ android:strokeWidth="0.251"
+ android:strokeAlpha="1"/>
+ <path
+ android:pathData="m16.64,17.64l0,3.21q0,0.41 0.16,0.58 0.17,0.17 0.5,0.17 0.31,0 0.61,-0.19 0.29,-0.19 0.46,-0.46l0,-3.33l0.98,0l0,4.6l-0.84,0l-0.06,-0.59q-0.26,0.34 -0.65,0.53 -0.39,0.18 -0.8,0.18 -0.67,0 -1,-0.37 -0.33,-0.37 -0.33,-1.01l0,-3.33z"
+ android:fillColor="#fdfdfd"
+ android:strokeWidth="0.265729"/>
+ <path
+ android:pathData="m12.52,24.71q0.3,0 0.54,0.09 0.25,0.09 0.49,0.35l-0.37,0.33q-0.14,-0.16 -0.28,-0.23 -0.14,-0.08 -0.34,-0.08 -0.27,0 -0.41,0.15 -0.14,0.15 -0.14,0.39 0,0.17 0.08,0.31 0.08,0.14 0.23,0.22 0.15,0.08 0.33,0.08l1.58,0l0,0.46l-0.47,0l0,1.56q-0.1,0.12 -0.26,0.24 -0.16,0.12 -0.41,0.21 -0.24,0.08 -0.58,0.08 -0.45,0 -0.75,-0.17 -0.3,-0.17 -0.44,-0.45 -0.15,-0.28 -0.15,-0.6 0,-0.46 0.24,-0.74 0.24,-0.28 0.66,-0.37l0,-0.02q-0.25,-0.06 -0.4,-0.18 -0.15,-0.12 -0.22,-0.3 -0.07,-0.17 -0.07,-0.37 0,-0.26 0.14,-0.48 0.14,-0.22 0.4,-0.35 0.26,-0.13 0.6,-0.13zM13.18,26.79l-0.58,0q-0.28,0 -0.45,0.12 -0.17,0.12 -0.26,0.32 -0.08,0.19 -0.08,0.39 0,0.2 0.08,0.37 0.08,0.17 0.24,0.28 0.16,0.1 0.42,0.1 0.21,0 0.37,-0.07 0.16,-0.07 0.26,-0.19z"
+ android:fillColor="#cccccc"
+ android:strokeWidth="0.265729"/>
+ <path
+ android:pathData="m22.12,15.25 l-0.61,-0.19 1.54,-3.37l-1.84,0l0,-0.51l2.49,0l0,0.47z"
+ android:fillColor="#cccccc"
+ android:strokeWidth="0.265729"/>
+</vector>
diff --git a/res/layout/launcher_activity.xml b/res/layout/launcher_activity.xml
index 4e0cc0c..67b3eb6 100644
--- a/res/layout/launcher_activity.xml
+++ b/res/layout/launcher_activity.xml
@@ -4,7 +4,7 @@
<TextView style="@style/paragraph" android:text="@string/launcher_description"/>
<Button style="@style/paragraph" android:text="@string/launcher_button_imesettings" android:onClick="launch_imesettings" android:layout_width="wrap_content"/>
<Button style="@style/paragraph" android:text="@string/launcher_button_imepicker" android:onClick="launch_imepicker" android:layout_width="wrap_content"/>
- <VideoView android:id="@+id/launcher_intro_video" android:layout_width="240dp" android:layout_height="wrap_content" android:layout_gravity="center"/>
+ <ImageView style="@style/anim" android:id="@+id/launcher_anim_swipe" android:background="@drawable/doc_key_u" android:src="@drawable/doc_anim_swipe"/>
<TextView android:id="@+id/launcher_tryhere_text" style="@style/paragraph" android:text="@string/launcher_tryhere"/>
<EditText android:id="@+id/launcher_tryhere_area" style="@style/paragraph" android:inputType="text" android:hint="@string/launcher_tryhere_hint" android:importantForAutofill="no"/>
<TextView style="@style/paragraph" android:text="@string/launcher_sourcecode"/>
diff --git a/res/raw/intro_video.mp4 b/res/raw/intro_video.mp4
deleted file mode 100644
index 9eefe52..0000000
--- a/res/raw/intro_video.mp4
+++ /dev/null
Binary files differ
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8705d98..56edbde 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -56,5 +56,12 @@
<item name="android:layout_marginHorizontal">16dp</item>
<item name="android:layout_gravity">center</item>
</style>
+ <style name="anim">
+ <item name="android:layout_width">100dp</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_marginVertical">16dp</item>
+ <item name="android:layout_gravity">center</item>
+ <item name="android:adjustViewBounds">true</item>
+ </style>
<style name="appTheme" parent="@android:style/Theme.DeviceDefault.DayNight"/>
</resources>
diff --git a/srcs/juloo.keyboard2/LauncherActivity.java b/srcs/juloo.keyboard2/LauncherActivity.java
index 2ca241a..57c5e2b 100644
--- a/srcs/juloo.keyboard2/LauncherActivity.java
+++ b/srcs/juloo.keyboard2/LauncherActivity.java
@@ -3,11 +3,14 @@ package juloo.keyboard2;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
+import android.graphics.drawable.Animatable;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build.VERSION;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
import android.provider.Settings;
import android.view.KeyEvent;
import android.view.Menu;
@@ -15,28 +18,49 @@ import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
+import android.widget.ImageView;
import android.widget.TextView;
-import android.widget.VideoView;
+import java.util.ArrayList;
+import java.util.List;
-public class LauncherActivity extends Activity
+public class LauncherActivity extends Activity implements Handler.Callback
{
/** Text is replaced when receiving key events. */
- VideoView _intro_video;
TextView _tryhere_text;
EditText _tryhere_area;
+ /** Periodically restart the animations. */
+ List<Animatable> _animations;
+ Handler _handler;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.launcher_activity);
- _intro_video = (VideoView)findViewById(R.id.launcher_intro_video);
_tryhere_text = (TextView)findViewById(R.id.launcher_tryhere_text);
_tryhere_area = (EditText)findViewById(R.id.launcher_tryhere_area);
if (VERSION.SDK_INT >= 28)
_tryhere_area.addOnUnhandledKeyEventListener(
this.new Tryhere_OnUnhandledKeyEventListener());
- setup_intro_video(_intro_video);
+ }
+
+ @Override
+ public void onStart()
+ {
+ super.onStart();
+ _animations = new ArrayList<Animatable>();
+ _animations.add(find_anim(R.id.launcher_anim_swipe));
+ _handler = new Handler(getMainLooper(), this);
+ _handler.sendEmptyMessageDelayed(0, 500);
+ }
+
+ @Override
+ public boolean handleMessage(Message _msg)
+ {
+ for (Animatable anim : _animations)
+ anim.start();
+ _handler.sendEmptyMessageDelayed(0, 3000);
+ return true;
}
@Override
@@ -70,31 +94,10 @@ public class LauncherActivity extends Activity
imm.showInputMethodPicker();
}
- static void setup_intro_video(final VideoView v)
+ Animatable find_anim(int id)
{
- if (VERSION.SDK_INT >= 26)
- v.setAudioFocusRequest(AudioManager.AUDIOFOCUS_NONE);
- v.setVideoURI(Uri.parse("android.resource://" +
- v.getContext().getPackageName() + "/" + R.raw.intro_video));
- v.setOnPreparedListener(new MediaPlayer.OnPreparedListener()
- {
- @Override
- public void onPrepared(MediaPlayer mp)
- {
- mp.setLooping(true);
- }
- });
- v.setOnErrorListener(new MediaPlayer.OnErrorListener()
- {
- @Override
- public boolean onError(MediaPlayer mp, int what, int extra)
- {
- v.stopPlayback();
- v.setVisibility(View.GONE);
- return true;
- }
- });
- v.start();
+ ImageView img = (ImageView)findViewById(id);
+ return (Animatable)img.getDrawable();
}
@TargetApi(28)
diff --git a/srcs/res/SvgToVector.java b/srcs/res/SvgToVector.java
new file mode 100644
index 0000000..78486fe
--- /dev/null
+++ b/srcs/res/SvgToVector.java
@@ -0,0 +1,32 @@
+package srcs.res;
+
+import com.android.ide.common.vectordrawable.Svg2Vector;
+import java.io.File;
+import java.io.FileOutputStream;
+
+/** Inspired from Bernard Ladenthin's answer:
+ https://stackoverflow.com/a/78898372 */
+public class SvgToVector
+{
+ public static void main(String[] args)
+ {
+ if (args.length != 2)
+ {
+ System.out.println("Usage: svg_to_vector <input_file> <output_file>");
+ return;
+ }
+ try
+ {
+ File input_file = new File(args[0]);
+ FileOutputStream output_stream = new FileOutputStream(args[1]);
+ String warnings;
+ warnings = Svg2Vector.parseSvgToXml(input_file, output_stream);
+ System.err.println(warnings);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ System.exit(2);
+ }
+ }
+}
diff --git a/srcs/res/doc_key.svg b/srcs/res/doc_key.svg
new file mode 100644
index 0000000..73e6a2a
--- /dev/null
+++ b/srcs/res/doc_key.svg
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="35mm"
+ height="40mm"
+ viewBox="0 0 35 40"
+ version="1.1"
+ id="svg1"
+ inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
+ sodipodi:docname="doc_key.svg"
+ inkscape:export-filename="key_u.svg"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview1"
+ pagecolor="#1b1b1b"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#1b1b1b"
+ inkscape:document-units="mm"
+ inkscape:zoom="4.6038101"
+ inkscape:cx="160.95364"
+ inkscape:cy="75.69817"
+ inkscape:window-width="2560"
+ inkscape:window-height="1440"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer2">
+ <inkscape:page
+ x="0"
+ y="0"
+ width="35"
+ height="40"
+ id="page1"
+ margin="0"
+ bleed="0"
+ inkscape:label="g" />
+ <inkscape:page
+ x="45"
+ y="0"
+ width="35"
+ height="40"
+ id="page2"
+ margin="0"
+ bleed="0"
+ inkscape:label="u" />
+ </sodipodi:namedview>
+ <defs
+ id="defs1" />
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="U"
+ style="display:inline">
+ <g
+ inkscape:label="Background"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="display:inline"
+ transform="translate(42.916821,-9.9998472)">
+ <rect
+ style="display:inline;fill:#333333;fill-opacity:1;stroke:#404040;stroke-width:0.251;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
+ id="rect1"
+ width="14.749311"
+ height="19.74971"
+ x="12.208678"
+ y="20.125347"
+ ry="1.1332803"
+ inkscape:label="rect1" />
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8.48996px;font-family:'Fira Code';-inkscape-font-specification:'Fira Code, @wght=500';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-variation-settings:'wght' 500;display:inline;fill:#fdfdfd;fill-opacity:1;stroke:none;stroke-width:0.265729;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
+ x="59.864403"
+ y="22.246796"
+ id="text1"
+ inkscape:label="text1"
+ inkscape:transform-center-x="-0.00074323194"
+ inkscape:transform-center-y="-0.001350474"
+ transform="scale(1.0003873,0.99961285)"><tspan
+ id="tspan1"
+ sodipodi:role="line"
+ x="59.864403"
+ y="22.246796"
+ style="stroke-width:0.265729">u</tspan><tspan
+ id="tspan2"
+ sodipodi:role="line"
+ x="59.864403"
+ y="32.859245"
+ style="stroke-width:0.265729" /></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.65997px;font-family:'Fira Code';-inkscape-font-specification:'Fira Code, @wght=500';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-variation-settings:'wght' 500;display:inline;fill:#cccccc;fill-opacity:1;stroke:none;stroke-width:0.265728;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
+ x="55.882885"
+ y="28.802013"
+ id="text3"
+ transform="scale(1.0003873,0.99961285)"
+ inkscape:label="text3"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.65997px;font-family:'Fira Code';-inkscape-font-specification:'Fira Code, @wght=500';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-variation-settings:'wght' 500;fill:#cccccc;fill-opacity:1;stroke-width:0.265729"
+ x="55.882885"
+ y="28.802013"
+ id="tspan4">&amp;</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.65997px;font-family:'Fira Code';-inkscape-font-specification:'Fira Code, @wght=500';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-variation-settings:'wght' 500;display:inline;fill:#cccccc;fill-opacity:1;stroke:none;stroke-width:0.265728;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
+ x="65.686905"
+ y="15.189408"
+ id="text5"
+ transform="scale(1.0003873,0.99961285)"><tspan
+ sodipodi:role="line"
+ id="tspan5"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.65997px;font-family:'Fira Code';-inkscape-font-specification:'Fira Code, @wght=500';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-variation-settings:'wght' 500;fill:#cccccc;fill-opacity:1;stroke-width:0.265729"
+ x="65.686905"
+ y="15.189408">7</tspan></text>
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer3"
+ inkscape:label="G"
+ style="display:inline">
+ <g
+ inkscape:label="Background"
+ inkscape:groupmode="layer"
+ id="g2"
+ style="display:inline">
+ <rect
+ style="display:inline;fill:#333333;fill-opacity:1;stroke:#404040;stroke-width:0.251;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
+ id="rect2"
+ width="14.749311"
+ height="19.74971"
+ x="10.125345"
+ y="10.125347"
+ ry="1.1332803"
+ inkscape:label="rect1" />
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.65997px;font-family:'Fira Code';-inkscape-font-specification:'Fira Code, @wght=500';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-variation-settings:'wght' 500;display:inline;fill:#cccccc;fill-opacity:1;stroke:none;stroke-width:0.265728;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
+ x="10.887006"
+ y="27.77977"
+ id="text3-3"
+ transform="scale(1.0003873,0.99961285)"
+ inkscape:label="text3"><tspan
+ sodipodi:role="line"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.65997px;font-family:'Fira Code';-inkscape-font-specification:'Fira Code, @wght=500';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-variation-settings:'wght' 500;fill:#cccccc;fill-opacity:1;stroke-width:0.265729"
+ x="10.887006"
+ y="27.77977"
+ id="tspan4-7">_</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8.48996px;font-family:'Fira Code';-inkscape-font-specification:'Fira Code, @wght=500';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-variation-settings:'wght' 500;display:inline;fill:#fdfdfd;fill-opacity:1;stroke:none;stroke-width:0.265729;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
+ x="14.868526"
+ y="22.204519"
+ id="text1-2"
+ inkscape:label="text1"
+ inkscape:transform-center-x="-0.00074323194"
+ inkscape:transform-center-y="-0.001350474"
+ transform="scale(1.0003873,0.99961285)"><tspan
+ id="tspan1-0"
+ sodipodi:role="line"
+ x="14.868526"
+ y="22.204519"
+ style="stroke-width:0.265729">g</tspan><tspan
+ id="tspan2-2"
+ sodipodi:role="line"
+ x="14.868526"
+ y="32.816971"
+ style="stroke-width:0.265729" /></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.65997px;font-family:'Fira Code';-inkscape-font-specification:'Fira Code, @wght=500';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-variation-settings:'wght' 500;display:inline;fill:#cccccc;fill-opacity:1;stroke:none;stroke-width:0.265728;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
+ x="20.691027"
+ y="15.147132"
+ id="text5-5"
+ transform="scale(1.0003873,0.99961285)"><tspan
+ sodipodi:role="line"
+ id="tspan5-9"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.65997px;font-family:'Fira Code';-inkscape-font-specification:'Fira Code, @wght=500';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-variation-settings:'wght' 500;fill:#cccccc;fill-opacity:1;stroke-width:0.265729"
+ x="20.691027"
+ y="15.147132">-</tspan></text>
+ </g>
+</svg>
diff --git a/srcs/res/gen_doc_key_drawables.sh b/srcs/res/gen_doc_key_drawables.sh
new file mode 100644
index 0000000..ff89793
--- /dev/null
+++ b/srcs/res/gen_doc_key_drawables.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+set -e
+cd "$(dirname $0)"
+
+DRAWABLE_DIR=../../res/drawable
+ANDROID_LIB=$ANDROID_SDK_ROOT/tools/lib
+
+first () { echo "$1"; }
+JAVA_ARGS=(
+ -classpath
+ "$(first $ANDROID_LIB/sdk-common-*.jar):$(first $ANDROID_LIB/common-*.jar)"
+)
+svg_to_vector ()
+{
+ java "${JAVA_ARGS[@]}" SvgToVector.java "$@"
+}
+
+TMP=`mktemp -d`
+trap "rm -r '$TMP'" EXIT
+set -x
+
+inkscape doc_key.svg -o "$TMP/doc_key_u.svg" -C --export-page 2 --export-plain-svg --export-text-to-path
+
+svg_to_vector "$TMP/doc_key_u.svg" "$DRAWABLE_DIR/doc_key_u.xml"