all:
mkdir -p bin/
- ../../bin/nitg --dir bin/ src/calculator_test.nit src/calculator_gtk.nit
+ ../../bin/nitg --dir bin/ src/calculator_gtk.nit src/calculator_test.nit
+
+android:
+ mkdir -p bin/ res/
+ ../../contrib/inkscape_tools/bin/svg_to_icons art/icon.svg --android --out res/
+ ../../bin/nitg -o bin/calculator.apk src/calculator_android.nit
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="512"
+ height="512"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.5 r10040"
+ sodipodi:docname="icon.svg">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.9899495"
+ inkscape:cx="58.64005"
+ inkscape:cy="380.00465"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1601"
+ inkscape:window-height="1316"
+ inkscape:window-x="2646"
+ inkscape:window-y="84"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-540.36218)">
+ <rect
+ style="fill:#4d4d4d;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none"
+ id="rect2987"
+ width="500"
+ height="500"
+ x="5.9999847"
+ y="546.36218"
+ rx="64"
+ ry="64" />
+ <rect
+ ry="48"
+ rx="48"
+ y="569.2193"
+ x="32.42857"
+ height="211.42856"
+ width="211.42856"
+ id="rect2989"
+ style="fill:#cccccc;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none" />
+ <rect
+ style="fill:#cccccc;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none"
+ id="rect2991"
+ width="211.42856"
+ height="211.42856"
+ x="268.14285"
+ y="569.2193"
+ rx="48"
+ ry="48" />
+ <rect
+ style="fill:#cccccc;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none"
+ id="rect2993"
+ width="211.42856"
+ height="211.42856"
+ x="32.42857"
+ y="806.36218"
+ rx="48"
+ ry="48" />
+ <rect
+ ry="48"
+ rx="48"
+ y="806.36218"
+ x="268.14285"
+ height="211.42856"
+ width="211.42856"
+ id="rect2995"
+ style="fill:#cccccc;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none" />
+ <text
+ xml:space="preserve"
+ style="font-size:269.1137085px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
+ x="299.74573"
+ y="770.06946"
+ id="text2997"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan2999"
+ x="299.74573"
+ y="770.06946">+</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ id="path3018"
+ style="font-size:269.1137085px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
+ d="m 313.14886,921.9317 0,-19.71047 121.41654,0 0,19.71047"
+ sodipodi:nodetypes="cccc" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path2995"
+ style="font-size:202.30386353px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
+ d="m 170.83821,674.43965 c -1e-4,11.65624 -0.8562,22.0941 -2.56831,31.31363 -1.64645,9.21961 -4.34646,17.02331 -8.10005,23.41114 -3.68792,6.38786 -8.46234,11.26106 -14.32328,14.61962 -5.86108,3.35856 -12.97332,5.03783 -21.33673,5.03784 -7.77084,-10e-6 -14.5538,-1.67928 -20.34892,-5.03784 -5.729341,-3.35856 -10.50376,-8.23176 -14.323274,-14.61962 -3.753702,-6.38783 -6.585427,-14.19153 -8.495182,-23.41114 -1.843926,-9.21953 -2.765883,-19.65739 -2.765873,-31.31363 -10e-6,-11.6561 0.823166,-22.09397 2.46953,-31.31364 1.712193,-9.21946 4.379282,-16.99024 8.001275,-23.31236 3.687807,-6.38772 8.4293,-11.26092 14.224494,-14.61961 5.86097,-3.35842 12.94028,-5.0377 21.23795,-5.03784 7.83658,1.4e-4 14.65247,1.67942 20.4477,5.03784 5.79508,3.29284 10.60243,8.13311 14.42206,14.52083 3.81944,6.32212 6.68409,14.09289 8.59396,23.31236 1.90967,9.21967 2.86455,19.69047 2.86465,31.41242 m -73.9871,0 c -2.8e-5,9.87818 0.493877,18.50506 1.481718,25.88067 0.987781,7.3757 2.568282,13.53305 4.741492,18.47208 2.17315,4.87322 5.00488,8.56105 8.49518,11.06349 3.55608,2.43662 7.86952,3.65492 12.94034,3.6549 5.07071,2e-5 9.38415,-1.21828 12.94033,-3.6549 3.55605,-2.43658 6.45363,-6.09148 8.69275,-10.96471 2.30481,-4.87317 3.95116,-10.9976 4.93906,-18.3733 1.05358,-7.44146 1.58041,-16.1342 1.5805,-26.07823 -9e-5,-9.87804 -0.52692,-18.50492 -1.5805,-25.88068 -0.9879,-7.37555 -2.63425,-13.49998 -4.93906,-18.3733 -2.23912,-4.87308 -5.1367,-8.52798 -8.69275,-10.96471 -3.55618,-2.43647 -7.86962,-3.65477 -12.94033,-3.6549 -5.07082,1.3e-4 -9.38426,1.21843 -12.94034,3.6549 -3.4903,2.43673 -6.32203,6.09163 -8.49518,10.96471 -2.17321,4.87332 -3.753711,10.99775 -4.741492,18.3733 -0.987841,7.37576 -1.481746,16.00264 -1.481718,25.88068" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path2997"
+ style="font-size:202.30386353px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
+ d="m 179.68975,735.88154 c -1e-5,-2.50244 0.32926,-4.60977 0.98782,-6.32199 0.65852,-1.77805 1.54755,-3.19391 2.66709,-4.2476 1.1195,-1.11949 2.43658,-1.90974 3.95125,-2.37074 1.51462,-0.52681 3.16097,-0.79023 4.93905,-0.79025 1.71218,2e-5 3.32561,0.26344 4.84028,0.79025 1.58047,0.461 2.93048,1.25125 4.05003,2.37074 1.11948,1.05369 2.00851,2.46955 2.66709,4.2476 0.65851,1.71222 0.98778,3.81955 0.98782,6.32199 -4e-5,2.43661 -0.32931,4.54394 -0.98782,6.322 -0.65858,1.71221 -1.54761,3.12807 -2.66709,4.24759 -1.11955,1.11952 -2.46956,1.94269 -4.05003,2.46953 -1.51467,0.52683 -3.1281,0.79024 -4.84028,0.79025 -1.77808,-10e-6 -3.42443,-0.26342 -4.93905,-0.79025 -1.51467,-0.52684 -2.83175,-1.35001 -3.95125,-2.46953 -1.11954,-1.11952 -2.00857,-2.53538 -2.66709,-4.24759 -0.65856,-1.77806 -0.98783,-3.88539 -0.98782,-6.322" />
+ <text
+ xml:space="preserve"
+ style="font-size:269.1137085px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
+ x="64.031456"
+ y="1006.8839"
+ id="text3013"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3015"
+ x="64.031456"
+ y="1006.8839">=</tspan></text>
+ </g>
+</svg>
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Android calculator application
+module calculator_android is
+ app_name "app.nit Calc."
+ app_version(0, 1, git_revision)
+ java_package "org.nitlanguage.calculator"
+
+ # Use a translucent background and lock in portrait mode
+ android_manifest_activity """
+ android:theme="@android:style/Theme.Holo.Wallpaper"
+ android:screenOrientation="portrait""""
+end
+
+import android
+import android::ui
+
+import calculator_logic
+
+redef class App
+ private var context = new CalculatorContext
+
+ # The main display, at the top of the screen
+ private var display: EditText
+
+ # Maps operators as `String` to their `Button`
+ private var op2but = new HashMap[String, Button]
+
+ # Has this window been initialized?
+ private var inited = false
+
+ redef fun init_window
+ do
+ super
+
+ if inited then return
+ inited = true
+
+ # Setup UI
+ var context = native_activity
+ var layout = new NativeLinearLayout(context)
+ layout.set_vertical
+
+ # Display screen
+ var display = new EditText
+ layout.add_view_with_weight(display.native, 1.0)
+ display.text_size = 36.0
+ self.display = display
+
+ # Buttons; numbers and operators
+ var ops = [["7", "8", "9", "+"],
+ ["4", "5", "6", "-"],
+ ["1", "2", "3", "*"],
+ ["0", ".", "C", "/"],
+ ["="]]
+
+ for line in ops do
+ var buts_layout = new NativeLinearLayout(context)
+ buts_layout.set_horizontal
+ layout.add_view_with_weight(buts_layout, 1.0)
+
+ for op in line do
+ var but = new Button
+ but.text = op
+ but.text_size = 40
+ buts_layout.add_view_with_weight(but.native, 1.0)
+ op2but[op] = but
+ end
+ end
+
+ context.content_view = layout
+ end
+
+ redef fun catch_event(event)
+ do
+ if event isa ClickEvent then
+ var sender = event.sender
+ var op = sender.text
+
+ if op == "." then
+ sender.enabled = false
+ context.switch_to_decimals
+ else if op.is_numeric then
+ var n = op.to_i
+ context.push_digit n
+ else
+ op2but["."].enabled = true
+ context.push_op op.chars.first
+ end
+
+ display.text = context.display_text
+ end
+ end
+end
--- /dev/null
+android:
+ mkdir -p bin/ res/
+ ../../../contrib/inkscape_tools/bin/svg_to_icons art/icon.svg --android --out res/
+ ../../../bin/nitg --dir bin/ src/ui_test.nit
+ adb install -r bin/ui_test.apk
+
+install: android
+ adb install -r bin/ui.apk
+
+clean:
+ rm -rf bin
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="512"
+ height="512"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.5 r10040"
+ sodipodi:docname="icon.svg">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#000000"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.98994949"
+ inkscape:cx="309.32788"
+ inkscape:cy="302.66563"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1311"
+ inkscape:window-height="960"
+ inkscape:window-x="3037"
+ inkscape:window-y="440"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-540.36218)">
+ <path
+ style="font-size:320.10992432px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:-55.78796005px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
+ d="M 116.09375,542.95593 C 95.970238,546.76157 75.866352,550.69664 55.75,554.54968 37.271066,654.48453 18.600595,754.38392 0.1875,854.33093 c 4.5670667,-0.0224 11.88925,-3.92217 14.989244,-1.88564 2.348097,12.30965 6.527711,24.13426 7.951802,36.55472 4.55774,23.64215 8.603147,47.44405 14.058954,70.86217 20.446729,-3.97475 40.892439,-7.95532 61.34375,-11.90625 0.806348,-6.01877 0.340643,-13.9623 0.6875,-18.84375 5.52874,12.6291 21.06261,15.98721 33.375,12.9375 18.4814,-4.95238 37.89007,-7.63966 56.75,-11.59375 -0.61826,-2.8783 4.8827,-0.79167 7.02749,-1.68038 14.16686,-1.03878 27.37606,-6.02271 41.41001,-7.38212 46.67851,-52.63829 -1.21449,-16.83212 30.65625,78.40625 17.19389,35.71392 58.70592,54.88142 97.40613,52.07812 21.55059,0.3486 43.28622,-5.5501 61.25012,-17.3281 0,7.9479 0,15.8958 0,23.8437 12.8637,-12.8518 32.06959,-10.4444 48.6875,-10.5625 12.08275,-0.4504 26.74579,-3.3417 33.09375,-14.75 2.84788,-8.0897 -0.35571,-16.9124 0.875,-25.25 0,-68.40622 0,-136.81247 0,-205.21872 -12.80081,2.58433 -25.80422,3.15955 -39,2.65625 -4.49542,-0.0745 -2.70343,-6.71819 -9.375,-2.8125 -14.41967,4.34039 -29.68137,2.22535 -44.53125,3.3125 -1.97068,-10.5394 -3.89878,-21.08667 -5.875,-31.625 43.54527,5.39974 90.52353,-22.98463 100.5,-66.625 11.74182,-45.53602 0.57063,-96.29858 -28.34375,-133 -18.76502,-21.52312 -47.96855,-34.21533 -76.77629,-30.338 -17.82405,1.69881 -35.28672,8.34732 -49.69246,18.74425 -12.97547,-7.86574 -27.99494,0.14323 -41.46466,1.78058 -8.5847,1.6356 -17.16935,3.2715 -25.75409,4.90692 2.25786,4.53438 -2.9589,1.63366 -5.28125,1.90625 -24.37251,-2.08163 -49.75835,4.12727 -69.28125,18.9375 -12.98942,-8.01584 -28.14096,-0.0134 -41.65179,1.65749 -7.26996,1.39739 -14.54254,2.78105 -21.81696,4.15501 C 140.087,578.41196 128.739,560.57354 117.4375,542.70593 l -0.98313,0.18291 z"
+ id="path3034"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccc" />
+ <text
+ transform="matrix(1.0172511,-0.19643198,0.1830023,0.94770357,0,0)"
+ sodipodi:linespacing="125%"
+ id="text2999"
+ y="843.55823"
+ x="-121.33073"
+ style="font-size:338.93121338px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:-0.06177186px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;letter-spacing:-59.06808853px;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
+ y="843.55823"
+ x="-121.33073"
+ id="tspan3001"
+ sodipodi:role="line">App</tspan></text>
+ <text
+ transform="matrix(1.0172511,-0.19643198,0.1830023,0.94770357,0,0)"
+ xml:space="preserve"
+ style="font-size:171.81326294px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="-132.6741"
+ y="958.20111"
+ id="text3003"
+ sodipodi:linespacing="125%"><tspan
+ style="letter-spacing:-22.57948303px"
+ sodipodi:role="line"
+ id="tspan3005"
+ x="-132.6741"
+ y="958.20111">nit</tspan></text>
+ <text
+ transform="scale(1.0360431,0.96521081)"
+ sodipodi:linespacing="125%"
+ id="text3007"
+ y="1056.8173"
+ x="253.55678"
+ style="font-size:265.30044556px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="1056.8173"
+ x="253.55678"
+ id="tspan3009"
+ sodipodi:role="line"
+ style="letter-spacing:-34.865448px">UI</tspan></text>
+ </g>
+</svg>
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Test for app.nit's UI services
+module ui_test is
+ app_name("app.nit UI test")
+ app_version(0, 1, git_revision)
+ java_package("org.nitlanguage.ui_test")
+ android_manifest_activity """android:theme="@android:style/Theme.Light""""
+end
+
+import android
+import android::ui
+import android::toast
+import android::notification
+
+redef class App
+
+ var but_notif: Button
+ var but_toast: Button
+
+ var notif: nullable Notification = null
+
+ var inited = false
+ redef fun init_window
+ do
+ super
+
+ if inited then return
+ inited = true
+
+ # Setup UI
+ var context = native_activity
+ var layout = new NativeLinearLayout(context)
+ layout.set_vertical
+
+ but_notif = new Button
+ but_notif.text = "Show Notification"
+ layout.add_view but_notif.native
+
+ but_toast = new Button
+ but_toast.text = "Show Toast"
+ layout.add_view but_toast.native
+
+ context.content_view = layout
+ end
+
+ fun act_notif
+ do
+ var notif = self.notif
+ if notif == null then
+ notif = new Notification("From app.nit", "Some content...")
+ notif.ticker = "Ticker text..."
+ notif.show
+ self.notif = notif
+ else
+ notif.cancel
+ self.notif = null
+ end
+ end
+
+ fun act_toast
+ do
+ toast("Sample toast from app.nit at {get_time}", false)
+ end
+
+ redef fun catch_event(event)
+ do
+ if event isa ClickEvent then
+ var sender = event.sender
+ if sender == but_notif then
+ act_notif
+ else if sender == but_toast then
+ act_toast
+ end
+ end
+ end
+end
}
`}
+# An Android activity context
+extern class NativeContext in "Java" `{ android.content.Context `}
+ super JavaObject
+end
+
+# A wrapper of context
+extern class NativeContextWrapper in "Java" `{ android.content.ContextWrapper `}
+ super NativeContext
+end
+
# Android SDK's `android.app.NativeActivity`.
#
# Can be used to get anything related to the `Context` of the activity in Java
# and as anchor to execute Java UI code.
extern class NativeActivity in "Java" `{ android.app.NativeActivity `}
- super JavaObject
+ super NativeContextWrapper
end
redef class App
# The `NativeActivity`, as in the Java object, associated to `self`
fun java_native_activity: NativeActivity `{ return recv->clazz; `}
- # Path to this application's internal data directory.
+ # Path to this application's internal data directory.
fun internal_data_path: NativeString `{ return (char*)recv->internalDataPath; `}
# Path to this application's external (removable/mountable) data directory.
- fun external_data_path: NativeString `{ return (char*)recv->externalDataPath; `}
+ fun external_data_path: NativeString `{ return (char*)recv->externalDataPath; `}
# The platform's SDK version code.
fun sdk_version: Int `{ return recv->sdkVersion; `}
--- /dev/null
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Native Java classes for notifications
+module native_notification is min_api_version 11
+
+import android::assets_and_resources
+
+in "Java" `{
+ import android.content.Context;
+ import android.app.NotificationManager;
+ import android.app.Notification;
+`}
+
+redef class NativeActivity
+ fun notification_manager: NativeNotificationManager in "Java" `{
+ return (NotificationManager)recv.getSystemService(Context.NOTIFICATION_SERVICE);
+ `}
+end
+
+extern class NativeNotificationManager in "Java" `{ android.app.NotificationManager `}
+
+ fun notify(tag: JavaString, id: Int, notif: NativeNotification) in "Java" `{
+ recv.notify(tag, (int)id, notif);
+ `}
+
+ fun cancel(tag: JavaString, id: Int) in "Java" `{ recv.cancel(tag, (int)id); `}
+
+ fun cancel_all in "Java" `{ recv.cancelAll(); `}
+end
+
+extern class NativeNotification in "Java" `{ android.app.Notification `}
+end
+
+extern class NativeNotificationBuilder in "Java" `{ android.app.Notification$Builder `}
+
+ new (context: NativeActivity) in "Java" `{ return new Notification.Builder(context); `}
+
+ fun create: NativeNotification in "Java" `{
+ // Deprecated since API 16, which introduces `build`,
+ // refinement and global compilation should prevent warnings.
+ return recv.getNotification();
+ `}
+
+ fun title=(value: JavaString) in "Java" `{ recv.setContentTitle(value); `}
+
+ fun text=(value: JavaString) in "Java" `{ recv.setContentText(value); `}
+
+ fun ticker=(value: JavaString) in "Java" `{ recv.setTicker(value); `}
+
+ fun small_icon=(value: Int) in "Java" `{ recv.setSmallIcon((int)value); `}
+
+ fun auto_cancel=(value: Bool) in "Java" `{ recv.setAutoCancel(value); `}
+
+ fun number=(value: Int) in "Java" `{ recv.setNumber((int)value); `}
+
+ fun ongoing=(value: Bool) in "Java" `{ recv.setOngoing(value); `}
+end
--- /dev/null
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services to show notification in the Android status bar
+#
+# ~~~~
+# # Create and show a notification
+# var notif = new Notification("My Title", "Some content")
+# notif.ticker = "Ticker text"
+# notif.show
+#
+# # Update the notification
+# notif.text = "New content!"
+# notif.ongoing = true # Make it un-dismissable
+# nofif.show
+#
+# # Hide the notification
+# notif.cancel
+# ~~~~
+#
+# For more information, see:
+# http://developer.android.com/guide/topics/ui/notifiers/notifications.html
+module notification
+
+import standard
+private import native_notification
+
+# An Android notification, shown at the top of the screen
+class Notification
+ # Title of this notification
+ var title: nullable Text is writable
+
+ # Text content of this notification
+ var text: nullable Text is writable
+
+ # Text to show in the bar as the notification appears
+ var ticker: nullable Text = null is writable
+
+ # Name of a resource found in the `res/drawable-*` folders to use for the small icon
+ #
+ # By default, we use the app's icon, named "icon". A valid icon must be used
+ # to display notifications.
+ var small_icon: nullable Text = null is writable
+
+ # Number to display on the bottom right part of the notification
+ var number: nullable Int = null is writable
+
+ # Is this notification ongoing? Not user dismissable.
+ var ongoing: Bool = false is writable
+
+ private var id: nullable Int = null
+ private var tag = "app.nit notification"
+
+ # Show the notification
+ fun show
+ do
+ sys.jni_env.push_local_frame(8)
+
+ var context = app.native_activity
+ var builder = new NativeNotificationBuilder(context)
+
+ # If no custom icon is specified, use app's
+ var small_icon = self.small_icon
+ if small_icon == null then small_icon = "icon"
+ var small_icon_id = app.resource_manager.other_id(small_icon.to_s, "drawable")
+ builder.small_icon = small_icon_id
+
+ # Other options
+ if title != null then builder.title = title.to_java_string
+ if text != null then builder.text = text.to_java_string
+ if ticker != null then builder.ticker = ticker.to_java_string
+ builder.ongoing = ongoing
+
+ var notif = builder.create
+ var manager = context.notification_manager
+
+ var id = self.id
+ if id == null then id = sys.next_notification_id
+ manager.notify(tag.to_java_string, id, notif)
+
+ self.id = id
+
+ sys.jni_env.pop_local_frame
+ end
+
+ # Was this notification shown with `show`?
+ #
+ # This does not indicates whether is has been dismissed or not. Only that
+ # it was shown at least once.
+ private fun was_shown: Bool do return id != null
+
+ # Cancel this notification and hide it if it is currently displayed
+ fun cancel
+ do
+ var id = self.id
+ if id != null then
+ sys.jni_env.push_local_frame(8)
+
+ var manager = app.native_activity.notification_manager
+ manager.cancel(tag.to_java_string, id)
+
+ self.id = null
+
+ sys.jni_env.pop_local_frame
+ end
+ end
+end
+
+redef class Sys
+ private var next_notification_id_cache = 0
+
+ # Returns a unique ID for new notifications
+ private fun next_notification_id: Int
+ do
+ var id = next_notification_id_cache
+ next_notification_id_cache = id + 1
+ return id
+ end
+end
--- /dev/null
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services to display a _toast_, a small popup on Android
+module toast
+
+import native_app_glue
+
+in "Java" `{
+ import android.widget.Toast;
+`}
+
+redef class App
+ # Display a _toast_ with `message`, for longer if `is_long`
+ fun toast(message: String, is_long: Bool)
+ do
+ var jstr = message.to_java_string
+ native_toast(jstr, is_long)
+ jstr.delete_local_ref
+ end
+
+ private fun native_toast(message: JavaString, is_long: Bool)
+ import native_activity in "Java" `{
+ final android.app.NativeActivity context = App_native_activity(recv);
+ final CharSequence final_message = message;
+ final int duration = is_long? Toast.LENGTH_LONG: Toast.LENGTH_SHORT;
+
+ context.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast toast = Toast.makeText(context, final_message, duration);
+ toast.show();
+ }
+ });
+ `}
+end
--- /dev/null
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Views and services to use the Android native user interface
+#
+# Events, such as a button click, come from the UI thread and are then
+# passed to the main thread. It is recommended to specialize one of the
+# methods of the main thread to customize the response to a given event.
+#
+# This graph shows the path of a button click:
+# ~~~
+# UI Thread # Main thread
+#
+# User
+# |
+# V
+# Button::click_ui --> Button::click
+# |
+# V
+# App::catch_event
+# ~~~
+module ui is min_api_version 14
+
+import native_app_glue
+import pthreads::concurrent_collections
+
+in "Java" `{
+ import android.app.NativeActivity;
+
+ import android.view.Gravity;
+ import android.view.MotionEvent;
+ import android.view.ViewGroup;
+ import android.view.ViewGroup.MarginLayoutParams;
+
+ import android.widget.Button;
+ import android.widget.LinearLayout;
+ import android.widget.GridLayout;
+ import android.widget.PopupWindow;
+ import android.widget.TextView;
+
+ import java.lang.*;
+ import java.util.*;
+`}
+
+# An event from the `app.nit` framework
+interface AppEvent
+ # Reaction to this event
+ fun react do end
+end
+
+# A control click event
+class ClickEvent
+ super AppEvent
+
+ # Sender of this event
+ var sender: Button
+
+ redef fun react do sender.click self
+end
+
+# Receiver of events not handled directly by the sender
+interface EventCatcher
+ fun catch_event(event: AppEvent) do end
+end
+
+redef class App
+ super EventCatcher
+
+ # Queue of events to be received by the main thread
+ var event_queue = new ConcurrentList[AppEvent]
+
+ # Call `react` on all `AppEvent` available in `event_queue`
+ protected fun loop_on_ui_callbacks
+ do
+ var queue = event_queue
+ while not queue.is_empty do
+ var event = queue.pop
+ event.react
+ end
+ end
+
+ redef fun run
+ do
+ loop
+ # Process Android events
+ poll_looper 100
+
+ # Process app.nit events
+ loop_on_ui_callbacks
+ end
+ end
+end
+
+redef extern class NativeActivity
+
+ # Fill this entire `NativeActivity` with `popup`
+ #
+ # This is a workaround for the use on `takeSurface` in `NativeActivity.java`
+ #
+ # TODO replace NativeActivity by our own NitActivity
+ private fun dedicate_to_popup(popup: NativePopupWindow, popup_layout: NativeViewGroup) in "Java" `{
+ final LinearLayout final_main_layout = new LinearLayout(recv);
+ final ViewGroup final_popup_layout = popup_layout;
+ final PopupWindow final_popup = popup;
+ final NativeActivity final_recv = recv;
+
+ recv.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ MarginLayoutParams params = new MarginLayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT);
+
+ final_recv.setContentView(final_main_layout, params);
+
+ final_popup.showAtLocation(final_popup_layout, Gravity.TOP, 0, 40);
+ }
+ });
+ `}
+
+ # Set the main layout of this activity
+ fun content_view=(layout: NativeViewGroup)
+ do
+ var popup = new NativePopupWindow(self)
+ popup.content_view = layout
+ dedicate_to_popup(popup, layout)
+ end
+
+ # Set the real content view of this activity, without hack
+ #
+ # TODO bring use this instead of the hack with `dedicate_to_pupup`
+ private fun real_content_view=(layout: NativeViewGroup) in "Java" `{
+ final ViewGroup final_layout = layout;
+ final NativeActivity final_recv = recv;
+
+ recv.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ final_recv.setContentView(final_layout);
+
+ final_layout.requestFocus();
+ }
+ });
+ `}
+end
+
+# An `Object` that raises events
+abstract class Eventful
+ var event_catcher: EventCatcher = app is lazy, writable
+end
+
+#
+## Nity classes and services
+#
+
+# An Android control with text
+abstract class TextView
+ super Finalizable
+ super Eventful
+
+ # Native Java variant to this Nity class
+ type NATIVE: NativeTextView
+
+ # The native Java object encapsulated by `self`
+ var native: NATIVE is noinit
+
+ # Get the text of this view
+ fun text: String
+ do
+ var jstr = native.text
+ var str = jstr.to_s
+ jstr.delete_local_ref
+ return str
+ end
+
+ # Set the text of this view
+ fun text=(value: Text)
+ do
+ var jstr = value.to_s.to_java_string
+ native.text = jstr
+ jstr.delete_local_ref
+ end
+
+ # Get whether this view is enabled or not
+ fun enabled: Bool do return native.enabled
+
+ # Set if this view is enabled
+ fun enabled=(val: Bool) do native.enabled = val
+
+ # Set the size of the text in this view at `dpi`
+ fun text_size=(dpi: Numeric) do native.text_size = dpi.to_f
+
+ private var finalized = false
+ redef fun finalize
+ do
+ if not finalized then
+ native.delete_global_ref
+ finalized = true
+ end
+ end
+end
+
+# An Android button
+class Button
+ super TextView
+
+ redef type NATIVE: NativeButton
+
+ init
+ do
+ var native = new NativeButton(app.native_activity, app.event_queue, self)
+ self.native = native.new_global_ref
+ end
+
+ # Click event on the Main thread
+ #
+ # By default, this method calls `app.catch_event`. It can be specialized
+ # with custom behavior or the receiver of `catch_event` can be changed
+ # with `event_catcher=`.
+ fun click(event: AppEvent) do event_catcher.catch_event(event)
+
+ # Click event on the UI thread
+ #
+ # This method is called on the UI thread and redirects the event to `click`
+ # throught `App::event_queue`. In most cases, you should implement `click`
+ # and leave `click_ui` as is.
+ fun click_ui do app.event_queue.add(new ClickEvent(self))
+end
+
+# An Android editable text field
+class EditText
+ super TextView
+
+ redef type NATIVE: NativeEditText
+
+ init
+ do
+ var native = new NativeEditText(app.native_activity)
+ self.native = native.new_global_ref
+ end
+end
+
+#
+## Native classes
+#
+
+# A `View` for Android
+extern class NativeView in "Java" `{ android.view.View `}
+ super JavaObject
+
+ fun minimum_width=(val: Int) in "Java" `{ recv.setMinimumWidth((int)val); `}
+ fun minimum_height=(val: Int) in "Java" `{ recv.setMinimumHeight((int)val); `}
+end
+
+# A collection of `NativeView`
+extern class NativeViewGroup in "Java" `{ android.view.ViewGroup `}
+ super NativeView
+
+ fun add_view(view: NativeView) in "Java" `{ recv.addView(view); `}
+end
+
+# A `NativeViewGroup` organized in a line
+extern class NativeLinearLayout in "Java" `{ android.widget.LinearLayout `}
+ super NativeViewGroup
+
+ new(context: NativeActivity) in "Java" `{ return new LinearLayout(context); `}
+
+ fun set_vertical in "Java" `{ recv.setOrientation(LinearLayout.VERTICAL); `}
+ fun set_horizontal in "Java" `{ recv.setOrientation(LinearLayout.HORIZONTAL); `}
+
+ redef fun add_view(view) in "Java"
+ `{
+ MarginLayoutParams params = new MarginLayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ recv.addView(view, params);
+ `}
+
+ fun add_view_with_weight(view: NativeView, weight: Float)
+ in "Java" `{
+ recv.addView(view, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT, (float)weight));
+ `}
+end
+
+# A `NativeViewGroup` organized as a grid
+extern class NativeGridLayout in "Java" `{ android.widget.GridLayout `}
+ super NativeViewGroup
+
+ new(context: NativeActivity) in "Java" `{ return new android.widget.GridLayout(context); `}
+
+ fun row_count=(val: Int) in "Java" `{ recv.setRowCount((int)val); `}
+
+ fun column_count=(val: Int) in "Java" `{ recv.setColumnCount((int)val); `}
+
+ redef fun add_view(view) in "Java" `{ recv.addView(view); `}
+end
+
+extern class NativePopupWindow in "Java" `{ android.widget.PopupWindow `}
+ super NativeView
+
+ new (context: NativeActivity) in "Java" `{
+ PopupWindow recv = new PopupWindow(context);
+ recv.setWindowLayoutMode(LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT);
+ recv.setClippingEnabled(false);
+ return recv;
+ `}
+
+ fun content_view=(layout: NativeViewGroup) in "Java" `{ recv.setContentView(layout); `}
+end
+
+extern class NativeTextView in "Java" `{ android.widget.TextView `}
+ super NativeView
+
+ new (context: NativeActivity) in "Java" `{ return new TextView(context); `}
+
+ fun text: JavaString in "Java" `{ return recv.getText().toString(); `}
+
+ fun text=(value: JavaString) in "Java" `{
+
+ android.util.Log.d("Nity", "1");
+ final TextView final_recv = recv;
+ final String final_value = value;
+
+ android.util.Log.d("Nity", "4");
+ ((NativeActivity)recv.getContext()).runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ android.util.Log.d("Nity", "-5");
+ android.util.Log.d("Nity", final_value);
+ android.util.Log.d("Nity", "-5.5");
+ final_recv.setText(final_value);
+ android.util.Log.d("Nity", "-6");
+ }
+ });
+ android.util.Log.d("Nity", "7");
+ `}
+
+ fun enabled: Bool in "Java" `{ return recv.isEnabled(); `}
+ fun enabled=(value: Bool) in "Java" `{
+ final TextView final_recv = recv;
+ final boolean final_value = value;
+
+ ((NativeActivity)recv.getContext()).runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ final_recv.setEnabled(final_value);
+ }
+ });
+ `}
+
+ fun gravity_center in "Java" `{
+ recv.setGravity(Gravity.CENTER);
+ `}
+
+ fun text_size=(dpi: Float) in "Java" `{
+ recv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_DIP, (float)dpi);
+ `}
+end
+
+extern class NativeEditText in "Java" `{ android.widget.EditText `}
+ super NativeTextView
+
+ redef type SELF: NativeEditText
+
+ new (context: NativeActivity) in "Java" `{ return new android.widget.EditText(context); `}
+
+ fun width=(val: Int) in "Java" `{ recv.setWidth((int)val); `}
+
+ fun input_type_text in "Java" `{ recv.setInputType(android.text.InputType.TYPE_CLASS_TEXT); `}
+
+ redef fun new_global_ref: SELF import sys, Sys.jni_env `{
+ Sys sys = NativeEditText_sys(recv);
+ JNIEnv *env = Sys_jni_env(sys);
+ return (*env)->NewGlobalRef(env, recv);
+ `}
+end
+
+extern class NativeButton in "Java" `{ android.widget.Button `}
+ super NativeTextView
+
+ redef type SELF: NativeButton
+
+ new (context: NativeActivity, queue: ConcurrentList[AppEvent], sender_object: Object) import Button.click_ui in "Java" `{
+ final int final_sender_object = sender_object;
+
+ return new Button(context){
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if(event.getAction() == MotionEvent.ACTION_DOWN) {
+ Button_click_ui(final_sender_object);
+ return true;
+ }
+ return false;
+ }
+ };
+ `}
+
+ redef fun new_global_ref: SELF import sys, Sys.jni_env `{
+ Sys sys = NativeButton_sys(recv);
+ JNIEnv *env = Sys_jni_env(sys);
+ return (*env)->NewGlobalRef(env, recv);
+ `}
+end