}
return afd;
`}
+
+ # HACK for bug #845
+ redef fun new_global_ref import sys, Sys.jni_env `{
+ Sys sys = NativeResources_sys(recv);
+ JNIEnv *env = Sys_jni_env(sys);
+ return (*env)->NewGlobalRef(env, recv);
+ `}
end
# Assets manager using a `NativeAssetManager` to manage android assets
# Open an asset using ACCESS_STREAMING mode, returning a NativeInputStream
fun open(file_name: String): NativeInputStream do
+ sys.jni_env.push_local_frame(1)
var return_value = native_assets_manager.open(file_name.to_java_string)
+ sys.jni_env.pop_local_frame
return return_value
end
# Open an asset using it's name and returning a NativeAssetFileDescriptor
# `file_name` is
fun open_fd(file_name: String): NativeAssetFileDescriptor do
- var return_value = native_assets_manager.open_fd(file_name.to_java_string)
+ sys.jni_env.push_local_frame(1)
+ var return_value = native_assets_manager.open_fd(file_name.to_java_string).new_global_ref
+ sys.jni_env.pop_local_frame
return return_value
end
# Return a bitmap from the assets
fun bitmap(name: String): NativeBitmap do
sys.jni_env.push_local_frame(1)
- var return_value = new NativeBitmap.from_stream(native_assets_manager.open(name.to_java_string))
+ var return_value = new NativeBitmap.from_stream(native_assets_manager.open(name.to_java_string)).new_global_ref
sys.jni_env.pop_local_frame
return return_value
end
new from_resources(res: NativeResources, id: Int) in "Java" `{ return BitmapFactory.decodeResource(res, (int)id); `}
fun width: Int in "Java" `{ return recv.getWidth(); `}
fun height: Int in "Java" `{ return recv.getHeight(); `}
+
+ # HACK for bug #845
+ redef fun new_global_ref import sys, Sys.jni_env `{
+ Sys sys = NativeResources_sys(recv);
+ JNIEnv *env = Sys_jni_env(sys);
+ return (*env)->NewGlobalRef(env, recv);
+ `}
+
end
# Android AssetFileDescriptor, can be retrieve by AssetManager and used to load a sound in a SoundPool
fun length: Int in "Java" `{ return (int)recv.getLength(); `}
fun start_offset: Int in "Java" `{ return (int)recv.getStartOffset(); `}
redef fun to_s: String import JavaString.to_s in "Java" `{ return JavaString_to_s(recv.toString()); `}
+
+ # HACK for bug #845
+ redef fun new_global_ref import sys, Sys.jni_env `{
+ Sys sys = NativeResources_sys(recv);
+ JNIEnv *env = Sys_jni_env(sys);
+ return (*env)->NewGlobalRef(env, recv);
+ `}
end
# Native class representing something drawable, can be retrieved from the resources
-# this file is part of NIT ( http://www.nitlanguage.org ).
+# This file is part of NIT ( http://www.nitlanguage.org ).
#
# Copyright 2014 Romain Chanoir <romain.chanoir@viacesi.fr>
#
# See the License for the specific language governing permissions and
# limitations under the License.
-# Android audio services
+# Android audio services, wraps a part of android audio API
#
-# You can get a sound by loading it with a `SoundPool` or a `MediaPlayer`
-# the recommended way to load a sound is by it's resource ID or it's name
-# other ways are for advanced use
+# For this example, the sounds "test_sound" and "test_music" are located in the "assets/sounds" folder,
+# they both have ".ogg" extension. "test_sound" is a short sound and "test_music" a music track
+# ~~~nitish
+# # Note that you need to specify the path from "assets" folder and the extension
+# var s = app.load_sound("sounds/test_sound.ogg")
+# var m = app.load_music("sounds/test_music.ogg")
+# s.play
+# m.play
+# ~~~
+#
+# Now, the sounds are in "res/raw"
+# ~~~nitish
+# s = app.load_sound_from_res("test_sound")
+# m = app.load_music_from_res("test_sound")
+# s.play
+# m.play
+# ~~~
+#
+# See http://developer.android.com/reference/android/media/package-summary.html for more infos
module audio
import java
import java::io
import assets_and_resources
-import app
+import app::audio
in "Java" `{
import android.media.MediaPlayer;
import android.media.SoundPool;
import java.io.IOException;
import android.media.AudioManager;
+ import android.media.AudioManager.OnAudioFocusChangeListener;
import android.content.Context;
import android.util.Log;
`}
+# FIXME: This listener is not working at the moment, but is needed to gain or give up the audio focus
+# of the application
+in "Java inner" `{
+ static OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
+ public void onAudioFocusChange(int focusChange) {
+ if(focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
+ }else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+ }else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
+ }
+ }
+ };
+`}
+
# AudioManager of the application, used to manage the audio mode
extern class NativeAudioManager in "Java" `{ android.media.AudioManager `}
super JavaObject
+ # Current audio mode.
+ # ( MODE_NORMAL = 0, MODE_RINGTONE = 1, MODE_IN_CALL = 2 or MODE_IN_COMMUNICATION = 3 )
fun mode: Int in "Java" `{ return recv.getMode(); `}
+
+ # Sets the audio mode.
+ # ( MODE_NORMAL = 0, MODE_RINGTONE = 1, MODE_IN_CALL = 2 or MODE_IN_COMMUNICATION = 3 )
fun mode=(i: Int) in "Java" `{ recv.setMode((int)i); `}
- fun wired_headset_on: Bool in "Java" `{ return recv.isWiredHeadsetOn(); `}
- fun wired_headset_on=(b: Bool) in "Java" `{ recv.setWiredHeadsetOn(b); `}
- fun speakerphone_on: Bool in "Java" `{ return recv.isSpeakerphoneOn(); `}
- fun speakerphone_on=(b: Bool) in "Java" `{ recv.setSpeakerphoneOn(b); `}
- fun manage_audio_mode in "Java" `{
- recv.setMode(0);
- if (recv.isWiredHeadsetOn()) {
- recv.setSpeakerphoneOn(false);
- } else {
- recv.setSpeakerphoneOn(true);
- }
+
+ # Sends a request to obtain audio focus
+ fun request_audio_focus: Int in "Java" `{
+ return recv.requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
`}
+ # Gives up audio focus
+ fun abandon_audio_focus: Int in "Java" `{ return recv.abandonAudioFocus(afChangeListener); `}
end
# Media Player from Java, used to play long sounds or musics, not simultaneously
# This is a low-level class, use `MediaPlater` instead
-extern class NativeMediaPlayer in "Java" `{ android.media.MediaPlayer `}
+private extern class NativeMediaPlayer in "Java" `{ android.media.MediaPlayer `}
super JavaObject
- new in "Java" `{ return new MediaPlayer(); `}
+ new in "Java" `{
+ MediaPlayer mp = new MediaPlayer();
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ return mp;
+ `}
fun start in "Java" `{ recv.start(); `}
fun prepare in "Java" `{
try {
# Sound Pool from Java, used to play sounds simultaneously
# This is a low-level class, use `SoundPool`instead
-extern class NativeSoundPool in "Java" `{ android.media.SoundPool `}
+private extern class NativeSoundPool in "Java" `{ android.media.SoundPool `}
super JavaObject
new(max_streams, stream_type, src_quality: Int) in "Java" `{
return new SoundSP(null, nsoundpool.load_asset_fd(afd, priority), self)
end
- # Load the sound from it's resource id
+ # Load the sound from its resource id
fun load_id(context: NativeActivity, id:Int): Sound do
return new SoundSP(null, nsoundpool.load_id(context, id, priority), self)
end
return nsoundpool.play(id, left_volume, right_volume, priority, looping, rate)
end
- # Load a sound by it's name in the resources, the sound must be in the `res/raw` folder
+ # Load a sound by its name in the resources, the sound must be in the `res/raw` folder
fun load_name(resource_manager: ResourcesManager, context: NativeActivity, sound: String): Sound do
var id = resource_manager.raw_id(sound)
return new SoundSP(id, nsoundpool.load_id(context, id, priority), self)
class MediaPlayer
private var nmedia_player: NativeMediaPlayer is noinit
+ # Used to control the state of the mediaplayer
+ private var is_prepared = false is writable
+
# The sound associated with this mediaplayer
- var sound: nullable Sound = null
+ var sound: nullable Sound = null is writable
# Create a new MediaPlayer, but no sound is attached, you'll need
# to use `load_sound` before using it
fun load_sound(id: Int, context: NativeActivity): Sound do
self.nmedia_player = self.nmedia_player.create(context, id)
self.sound = new SoundMP(id, self)
+ self.is_prepared = true
return self.sound.as(not null)
end
- # Starts or resume playback
+ # Starts or resumes playback
# REQUIRE `self.sound != null`
fun start do
- assert sound != null
+ if not is_prepared then prepare
nmedia_player.start
end
# Stops playback after playback has been stopped or paused
# REQUIRE `self.sound != null`
fun stop do
- assert sound != null
+ is_prepared = false
nmedia_player.stop
end
fun prepare do
assert sound != null
nmedia_player.prepare
+ is_prepared = true
end
# Pauses playback
# Reset MediaPlayer to its initial state
fun reset do nmedia_player.reset
- # Sets the datasource (file-pathor http/rtsp URL) to use
+ # Sets the datasource (file-path or http/rtsp URL) to use
fun data_source(path: String): Sound do
sys.jni_env.push_local_frame(1)
nmedia_player.data_source_path(path.to_java_string)
fun stream_type=(stream_type: Int) do nmedia_player.stream_type = stream_type
end
-# Represents an android sound that can be played by a SoundPool or a MediaPlayer
-# The only way to get a sound is by a MediaPlayer, a SoundPool, or the App
-abstract class Sound
+redef class Sound
- # The resource ID of this sound
+ # Resource ID of this sound
var id: nullable Int
-
- fun play is abstract
end
# Sound implemented with a SoundPool
end
redef fun play do soundpool.play(soundpool_id)
+ redef fun pause do soundpool.pause_stream(soundpool_id)
+ redef fun resume do soundpool.resume(soundpool_id)
end
# Sound Implemented with a MediaPlayer
self.media_player = media_player
end
- redef fun play do self.media_player.start
+ redef fun play do media_player.start
+ redef fun pause do media_player.pause
+ redef fun resume do play
end
redef class App
+ # Sounds handled by the application, when you load a sound, it's added to this list.
+ # This array is used in `pause` and `resume`
+ private var sounds = new Array[Sound]
+
+ # Returns the default MediaPlayer of the application.
+ # When you load a music, it goes in this MediaPlayer.
+ # Use it for advanced sound management
fun default_mediaplayer: MediaPlayer is cached do return new MediaPlayer
+
+ # Returns the default MediaPlayer of the application.
+ # When you load a short sound (not a music), it's added to this soundpool.
+ # Use it for advanced sound management.
fun default_soundpool: SoundPool is cached do return new SoundPool
# Get the native audio manager
- private fun audio_manager: NativeAudioManager import native_activity in "Java" `{
+ fun audio_manager: NativeAudioManager import native_activity in "Java" `{
return (AudioManager)App_native_activity(recv).getSystemService(Context.AUDIO_SERVICE);
`}
- # Manages whether the app sound need to be in headphones mode or not
- # TODO: this method is not ideal, need to find a better way
- fun manage_audio_mode import native_activity in "Java" `{
- AudioManager manager = (AudioManager)App_native_activity(recv).getSystemService(Context.AUDIO_SERVICE);
- manager.setMode(0);
- if (manager.isWiredHeadsetOn()) {
- manager.setSpeakerphoneOn(false);
- } else {
- manager.setSpeakerphoneOn(true);
- }
+ # Sets the stream of the app to STREAM_MUSIC.
+ # STREAM_MUSIC is the default stream used by android apps.
+ private fun manage_audio_stream import native_activity, native_app_glue in "Java" `{
+ App_native_activity(recv).setVolumeControlStream(AudioManager.STREAM_MUSIC);
`}
- # Retrieve a sound with a soundpool in the `assets` folder using it's name
+ # Retrieves a sound with a soundpool in the `assets` folder using its name.
# Used to play short songs, can play multiple sounds simultaneously
- fun load_sound(path: String): Sound do
- return default_soundpool.load_asset_fd(asset_manager.open_fd(path))
+ redef fun load_sound(path: String): Sound do
+ return add_to_sounds(default_soundpool.load_asset_fd(asset_manager.open_fd(path)))
end
- # Retrieve a music with a media player in the `assets`folder using it's name
+ # Retrieves a music with a media player in the `assets` folder using its name.
# Used to play long sounds or musics, can't play multiple sounds simultaneously
fun load_music(path: String): Sound do
var fd = asset_manager.open_fd(path)
- var sound = default_mediaplayer.data_source_fd(fd.file_descriptor, fd.start_offset, fd.length)
- return sound
+ return add_to_sounds(default_mediaplayer.data_source_fd(fd.file_descriptor, fd.start_offset, fd.length))
end
- # same as `load_sound` but load the sound from the `res\raw` folder
+ # Same as `load_sound` but load the sound from the `res/raw` folder
fun load_sound_from_res(sound_name: String): Sound do
- return default_soundpool.load_name(resource_manager,self.native_activity, sound_name)
+ return add_to_sounds(default_soundpool.load_name(resource_manager,self.native_activity, sound_name))
end
- # same as `load_music` but load the sound from the `res\raw` folder
+ # Same as `load_music` but load the sound from the `res/raw` folder
fun load_music_from_res(music: String): Sound do
- return default_mediaplayer.load_sound(resource_manager.raw_id(music), self.native_activity)
+ return add_to_sounds(default_mediaplayer.load_sound(resource_manager.raw_id(music), self.native_activity))
end
+ # Factorizes `sounds.add` to use it in `load_music`, `load_sound`, `load_music_from_res` and `load_sound_from_res`
+ private fun add_to_sounds(sound: Sound): Sound do
+ sounds.add(sound)
+ return sound
+ end
+
+ redef fun pause do
+ for s in sounds do s.pause
+ audio_manager.abandon_audio_focus
+ end
+
+ redef fun init_window do
+ super
+ audio_manager.request_audio_focus
+ manage_audio_stream
+ end
+
+ redef fun resume do
+ super
+ audio_manager.request_audio_focus
+ for s in sounds do s.resume
+ end
end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+# Enable caching on Github API accesses.
+#
+# If `GithubAPI::enable_cache` is set to true then Github JSON responses
+# will be cached locally using `JsonStore`.
+#
+# Cache can be used to limit the number of access on the API and lighten
+# the rate limit on your github key.
+#
+# Usage:
+#
+# ~~~
+# var api = new GithubAPI(get_github_oauth)
+# api.enable_cache = true
+#
+# var name = "privat/nit"
+# assert not api.has_cache(name)
+# var repo = api.load_repo(name) # load from GitHub
+# assert api.has_cache(name)
+# repo = api.load_repo(name) # load from cache
+#
+# api.clear_cache
+# assert not api.has_cache(name)
+# ~~~
+module cache
+
+intrude import github::api
+import json::store
+
+redef class GithubAPI
+
+ # Enable caching for this client.
+ # Default is `false`.
+ var enable_cache = false is writable
+
+ # JsonStore used to cache data.
+ #
+ # Default directory is `".github_data/"`.
+ var store = new JsonStore(".github_data/") is writable, lazy
+
+ # Delete the cache directory.
+ fun clear_cache do store.clear
+
+ # If no cache data is found for `key` then json is loaded from Github API.
+ redef fun load_from_github(key) do
+ if not enable_cache then return super
+ if store.has_key(key) then
+ message(1, "Get {key} (cache)")
+ was_error = false
+ return store.load_object(key)
+ end
+ var obj = super
+ if not was_error then cache(key, obj)
+ return obj
+ end
+
+ # Save `json` data in cache under `key`.
+ private fun cache(key: String, json: JsonObject) do
+ message(2, "Cache key {key}")
+ store.store_object(key, json)
+ end
+
+ # Check if a cache file exists for `key`.
+ fun has_cache(key: String): Bool do
+ return store.has_key(key)
+ end
+end