├── .travis.yml ├── GroovyDroid ├── build.gradle ├── libs │ └── android-support-v4.jar └── src │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ ├── groovy │ │ └── lang │ │ │ └── GrooidClassLoader.java │ └── me │ │ └── champeau │ │ └── groovydroid │ │ ├── GrooidShell.java │ │ ├── GroovyActivity.groovy │ │ └── util │ │ ├── SystemUiHider.java │ │ ├── SystemUiHiderBase.java │ │ └── SystemUiHiderHoneycomb.java │ └── res │ ├── drawable-hdpi │ ├── groovy.png │ └── ic_launcher.png │ ├── drawable-mdpi │ ├── groovy.png │ └── ic_launcher.png │ ├── drawable-xhdpi │ ├── groovy.png │ └── ic_launcher.png │ ├── drawable-xxhdpi │ ├── groovy.png │ └── ic_launcher.png │ ├── layout │ └── groovy_main.xml │ ├── menu │ └── groovy.xml │ ├── values-sw600dp │ └── dimens.xml │ ├── values-sw720dp-land │ └── dimens.xml │ ├── values-v11 │ └── styles.xml │ ├── values-v14 │ └── styles.xml │ └── values │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── LICENSE.txt ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── groovylib ├── .gitignore ├── build.gradle ├── proguard-rules.txt └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── me │ └── champeau │ └── testlibrary │ └── groovylib │ └── GroovyUtils.groovy └── settings.gradle /.travis.yml: -------------------------------------------------------------------------------- 1 | script: ./gradlew -s clean assembleDebug 2 | language: android 3 | android: 4 | components: 5 | - build-tools-19.1.0 6 | - android-19 7 | - sysimg-19 8 | - extra-android-support 9 | 10 | -------------------------------------------------------------------------------- /GroovyDroid/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | dependencies { 6 | classpath 'com.android.tools.build:gradle:0.12.+' 7 | classpath 'me.champeau.gradle:gradle-groovy-android-plugin:0.3.0' 8 | } 9 | } 10 | 11 | apply plugin: 'android' 12 | apply plugin: 'me.champeau.gradle.groovy-android' 13 | 14 | repositories { 15 | mavenLocal() 16 | maven { 17 | url='https://oss.jfrog.org/oss-snapshot-local/' 18 | } 19 | jcenter() 20 | } 21 | 22 | 23 | 24 | dependencies { 25 | compile 'com.google.android.tools:dx:1.7' 26 | compile 'org.codehaus.groovy:groovy:2.4.0-SNAPSHOT:grooid' 27 | compile project(':groovylib') 28 | } 29 | 30 | android { 31 | compileSdkVersion 17 32 | buildToolsVersion "19.1.0" 33 | 34 | defaultConfig { 35 | minSdkVersion 17 36 | targetSdkVersion 17 37 | } 38 | 39 | signingConfigs { 40 | release { 41 | storeFile file(System.getenv("KEYSTORE")?:debug.storeFile) 42 | storePassword System.getenv("KEYSTORE_PASSWORD")?:'foo' 43 | keyAlias System.getenv("KEY_ALIAS")?:'foo' 44 | keyPassword System.getenv("KEY_PASSWORD")?:'foo' 45 | } 46 | } 47 | 48 | buildTypes { 49 | debug { 50 | runProguard false 51 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 52 | } 53 | release { 54 | runProguard true 55 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 56 | signingConfig signingConfigs.release 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /GroovyDroid/libs/android-support-v4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/melix/grooidshell-example/ee781ba1a07fb9ae60304236c51f93a66232c7c6/GroovyDroid/libs/android-support-v4.jar -------------------------------------------------------------------------------- /GroovyDroid/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /GroovyDroid/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/melix/grooidshell-example/ee781ba1a07fb9ae60304236c51f93a66232c7c6/GroovyDroid/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /GroovyDroid/src/main/java/groovy/lang/GrooidClassLoader.java: -------------------------------------------------------------------------------- 1 | package groovy.lang; 2 | 3 | import org.codehaus.groovy.ast.ClassNode; 4 | import org.codehaus.groovy.control.CompilationUnit; 5 | import org.codehaus.groovy.control.CompilerConfiguration; 6 | import org.codehaus.groovy.control.SourceUnit; 7 | 8 | import java.security.AccessController; 9 | import java.security.PrivilegedAction; 10 | 11 | import groovyjarjarasm.asm.ClassWriter; 12 | 13 | /** 14 | * An extension of {@link groovy.lang.GroovyClassLoader} which handles the fact 15 | * that classes generated by Groovy will not be directly loadable by the dex 16 | * class loader. 17 | * 18 | * @author Cédric Champeau 19 | */ 20 | public class GrooidClassLoader extends GroovyClassLoader { 21 | 22 | public GrooidClassLoader(ClassLoader loader, CompilerConfiguration config) { 23 | super(loader, config); 24 | } 25 | 26 | @Override 27 | protected ClassCollector createCollector(CompilationUnit unit, SourceUnit su) { 28 | InnerLoader loader = AccessController.doPrivileged(new PrivilegedAction() { 29 | public InnerLoader run() { 30 | return new InnerLoader(GrooidClassLoader.this); 31 | } 32 | }); 33 | return new ClassCollector(loader, unit, su) { 34 | @Override 35 | protected Class onClassNode(ClassWriter classWriter, ClassNode classNode) { 36 | try { 37 | return super.onClassNode(classWriter, classNode); 38 | } catch (Exception e) { 39 | return null; 40 | } 41 | } 42 | }; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /GroovyDroid/src/main/java/me/champeau/groovydroid/GrooidShell.java: -------------------------------------------------------------------------------- 1 | package me.champeau.groovydroid; 2 | 3 | import android.util.Log; 4 | 5 | import com.android.dx.Version; 6 | import com.android.dx.dex.DexFormat; 7 | import com.android.dx.dex.DexOptions; 8 | import com.android.dx.dex.cf.CfOptions; 9 | import com.android.dx.dex.cf.CfTranslator; 10 | import com.android.dx.dex.code.PositionList; 11 | import com.android.dx.dex.file.ClassDefItem; 12 | import com.android.dx.dex.file.DexFile; 13 | 14 | import org.codehaus.groovy.control.BytecodeProcessor; 15 | import org.codehaus.groovy.control.CompilerConfiguration; 16 | 17 | import java.io.ByteArrayOutputStream; 18 | import java.io.File; 19 | import java.io.FileOutputStream; 20 | import java.io.IOException; 21 | import java.io.OutputStreamWriter; 22 | import java.util.LinkedHashMap; 23 | import java.util.LinkedHashSet; 24 | import java.util.Map; 25 | import java.util.Set; 26 | import java.util.UUID; 27 | import java.util.jar.Attributes; 28 | import java.util.jar.JarEntry; 29 | import java.util.jar.JarOutputStream; 30 | import java.util.jar.Manifest; 31 | 32 | import dalvik.system.DexClassLoader; 33 | import groovy.lang.GrooidClassLoader; 34 | import groovy.lang.Script; 35 | 36 | /** 37 | * A shell capable of executing Groovy scripts at runtime, on an Android device. 38 | * 39 | * @author Cédric Champeau 40 | */ 41 | public class GrooidShell { 42 | 43 | private static final String DEX_IN_JAR_NAME = "classes.dex"; 44 | private static final Attributes.Name CREATED_BY = new Attributes.Name("Created-By"); 45 | 46 | private final DexOptions dexOptions; 47 | private final CfOptions cfOptions; 48 | 49 | private final File tmpDynamicFiles; 50 | private final ClassLoader classLoader; 51 | 52 | public GrooidShell(File tmpDir, ClassLoader parent) { 53 | tmpDynamicFiles = tmpDir; 54 | classLoader = parent; 55 | dexOptions = new DexOptions(); 56 | dexOptions.targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES; 57 | cfOptions = new CfOptions(); 58 | cfOptions.positionInfo = PositionList.LINES; 59 | cfOptions.localInfo = true; 60 | cfOptions.strictNameCheck = true; 61 | cfOptions.optimize = false; 62 | cfOptions.optimizeListFile = null; 63 | cfOptions.dontOptimizeListFile = null; 64 | cfOptions.statistics = false; 65 | } 66 | 67 | 68 | public EvalResult evaluate(String scriptText) { 69 | long sd = System.nanoTime(); 70 | final Set classNames = new LinkedHashSet(); 71 | final DexFile dexFile = new DexFile(dexOptions); 72 | CompilerConfiguration config = new CompilerConfiguration(); 73 | config.setBytecodePostprocessor(new BytecodeProcessor() { 74 | @Override 75 | public byte[] processBytecode(String s, byte[] bytes) { 76 | ClassDefItem classDefItem = CfTranslator.translate(s+".class", bytes, cfOptions, dexOptions); 77 | dexFile.add(classDefItem); 78 | classNames.add(s); 79 | return bytes; 80 | } 81 | }); 82 | 83 | GrooidClassLoader gcl = new GrooidClassLoader(this.classLoader, config); 84 | try { 85 | gcl.parseClass(scriptText); 86 | } catch (Throwable e) { 87 | Log.e("GrooidShell","Dynamic loading failed!",e); 88 | } 89 | byte[] dalvikBytecode = new byte[0]; 90 | try { 91 | dalvikBytecode = dexFile.toDex(new OutputStreamWriter(new ByteArrayOutputStream()), false); 92 | } catch (IOException e) { 93 | Log.e("GrooidShell", "Unable to convert to Dalvik", e); 94 | } 95 | 96 | Map classes = defineDynamic(classNames, dalvikBytecode); 97 | long compilationTime = System.nanoTime()-sd; 98 | long execTime = 0; 99 | Object result = null; 100 | for (Class scriptClass : classes.values()) { 101 | if (Script.class.isAssignableFrom(scriptClass)) { 102 | sd = System.nanoTime(); 103 | Script script = null; 104 | try { 105 | script = (Script) scriptClass.newInstance(); 106 | } catch (InstantiationException e) { 107 | Log.e("GroovyDroidShell", "Unable to create script",e); 108 | } catch (IllegalAccessException e) { 109 | Log.e("GroovyDroidShell", "Unable to create script", e); 110 | } 111 | result = script.run(); 112 | execTime = System.nanoTime()-sd; 113 | break; 114 | } 115 | } 116 | return new EvalResult(compilationTime, execTime, result); 117 | } 118 | 119 | 120 | private Map defineDynamic(Set classNames, byte[] dalvikBytecode) { 121 | File tmpDex = new File(tmpDynamicFiles, UUID.randomUUID().toString()+".jar"); 122 | Map result = new LinkedHashMap(); 123 | try { 124 | FileOutputStream fos = new FileOutputStream(tmpDex); 125 | JarOutputStream jar = new JarOutputStream(fos, makeManifest()); 126 | JarEntry classes = new JarEntry(DEX_IN_JAR_NAME); 127 | classes.setSize(dalvikBytecode.length); 128 | jar.putNextEntry(classes); 129 | jar.write(dalvikBytecode); 130 | jar.closeEntry(); 131 | jar.finish(); 132 | jar.flush(); 133 | fos.flush(); 134 | fos.close(); 135 | jar.close(); 136 | DexClassLoader loader = new DexClassLoader(tmpDex.getAbsolutePath(), tmpDynamicFiles.getAbsolutePath(), null, classLoader); 137 | for (String className : classNames) { 138 | result.put(className, loader.loadClass(className)); 139 | } 140 | return result; 141 | } catch (Throwable e) { 142 | Log.e("DynamicLoading", "Unable to load class",e); 143 | } finally { 144 | tmpDex.delete(); 145 | } 146 | return null; 147 | } 148 | 149 | private static Manifest makeManifest() throws IOException { 150 | Manifest manifest = new Manifest(); 151 | Attributes attribs = manifest.getMainAttributes(); 152 | attribs.put(Attributes.Name.MANIFEST_VERSION, "1.0"); 153 | attribs.put(CREATED_BY, "dx " + Version.VERSION); 154 | attribs.putValue("Dex-Location", DEX_IN_JAR_NAME); 155 | return manifest; 156 | } 157 | 158 | public static class EvalResult { 159 | final long compilationTime; 160 | final long execTime; 161 | final Object result; 162 | 163 | public EvalResult(long compilationTime, long execTime, Object result) { 164 | this.compilationTime = compilationTime; 165 | this.execTime = execTime; 166 | this.result = result; 167 | } 168 | 169 | @Override 170 | public String toString() { 171 | StringBuilder sb = new StringBuilder(); 172 | sb.append("Compilation time = ").append(compilationTime / 1000000).append("ms"); 173 | sb.append("\n"); 174 | sb.append("Execution time = ").append(execTime / 1000000).append("ms"); 175 | sb.append("\n"); 176 | sb.append("Result = ").append(result); 177 | return sb.toString(); 178 | } 179 | } 180 | 181 | } 182 | -------------------------------------------------------------------------------- /GroovyDroid/src/main/java/me/champeau/groovydroid/GroovyActivity.groovy: -------------------------------------------------------------------------------- 1 | package me.champeau.groovydroid 2 | 3 | import android.app.Activity 4 | import android.os.Bundle 5 | import android.util.Log 6 | import android.view.Menu 7 | import android.view.View 8 | import android.widget.EditText 9 | import groovy.transform.CompileStatic 10 | import me.champeau.testlibrary.groovylib.GroovyUtils 11 | 12 | @CompileStatic 13 | class GroovyActivity extends Activity { 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState) 18 | setContentView(R.layout.groovy_main) 19 | Log.d("GroovyActivity", GroovyUtils.hello()) 20 | } 21 | 22 | @Override 23 | public boolean onCreateOptionsMenu(Menu menu) { 24 | // Inflate the menu; this adds items to the action bar if it is present. 25 | menuInflater.inflate(R.menu.groovy, menu) 26 | true 27 | } 28 | 29 | public void executeCode(View view) { 30 | def resultView = (EditText) findViewById(R.id.resultView) 31 | resultView.setText(generateMessage()) 32 | } 33 | 34 | String generateMessage() { 35 | GrooidShell shell = new GrooidShell(applicationContext.getDir("dynclasses", 0), this.classLoader) 36 | 37 | def code = (EditText) findViewById(R.id.editText) 38 | shell.evaluate(code.text.toString()) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /GroovyDroid/src/main/java/me/champeau/groovydroid/util/SystemUiHider.java: -------------------------------------------------------------------------------- 1 | package me.champeau.groovydroid.util; 2 | 3 | import android.app.Activity; 4 | import android.os.Build; 5 | import android.view.View; 6 | 7 | /** 8 | * A utility class that helps with showing and hiding system UI such as the 9 | * status bar and navigation/system bar. This class uses backward-compatibility 10 | * techniques described in 12 | * Creating Backward-Compatible UIs to ensure that devices running any 13 | * version of ndroid OS are supported. More specifically, there are separate 14 | * implementations of this abstract class: for newer devices, 15 | * {@link #getInstance} will return a {@link SystemUiHiderHoneycomb} instance, 16 | * while on older devices {@link #getInstance} will return a 17 | * {@link SystemUiHiderBase} instance. 18 | *

19 | * For more on system bars, see System Bars. 22 | * 23 | * @see android.view.View#setSystemUiVisibility(int) 24 | * @see android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN 25 | */ 26 | public abstract class SystemUiHider { 27 | /** 28 | * When this flag is set, the 29 | * {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN} 30 | * flag will be set on older devices, making the status bar "float" on top 31 | * of the activity layout. This is most useful when there are no controls at 32 | * the top of the activity layout. 33 | *

34 | * This flag isn't used on newer devices because the action 36 | * bar, the most important structural element of an Android app, should 37 | * be visible and not obscured by the system UI. 38 | */ 39 | public static final int FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES = 0x1; 40 | 41 | /** 42 | * When this flag is set, {@link #show()} and {@link #hide()} will toggle 43 | * the visibility of the status bar. If there is a navigation bar, show and 44 | * hide will toggle low profile mode. 45 | */ 46 | public static final int FLAG_FULLSCREEN = 0x2; 47 | 48 | /** 49 | * When this flag is set, {@link #show()} and {@link #hide()} will toggle 50 | * the visibility of the navigation bar, if it's present on the device and 51 | * the device allows hiding it. In cases where the navigation bar is present 52 | * but cannot be hidden, show and hide will toggle low profile mode. 53 | */ 54 | public static final int FLAG_HIDE_NAVIGATION = FLAG_FULLSCREEN | 0x4; 55 | 56 | /** 57 | * The activity associated with this UI hider object. 58 | */ 59 | protected Activity mActivity; 60 | 61 | /** 62 | * The view on which {@link View#setSystemUiVisibility(int)} will be called. 63 | */ 64 | protected View mAnchorView; 65 | 66 | /** 67 | * The current UI hider flags. 68 | * 69 | * @see #FLAG_FULLSCREEN 70 | * @see #FLAG_HIDE_NAVIGATION 71 | * @see #FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES 72 | */ 73 | protected int mFlags; 74 | 75 | /** 76 | * The current visibility callback. 77 | */ 78 | protected OnVisibilityChangeListener mOnVisibilityChangeListener = sDummyListener; 79 | 80 | /** 81 | * Creates and returns an instance of {@link SystemUiHider} that is 82 | * appropriate for this device. The object will be either a 83 | * {@link SystemUiHiderBase} or {@link SystemUiHiderHoneycomb} depending on 84 | * the device. 85 | * 86 | * @param activity The activity whose window's system UI should be 87 | * controlled by this class. 88 | * @param anchorView The view on which 89 | * {@link View#setSystemUiVisibility(int)} will be called. 90 | * @param flags Either 0 or any combination of {@link #FLAG_FULLSCREEN}, 91 | * {@link #FLAG_HIDE_NAVIGATION}, and 92 | * {@link #FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES}. 93 | */ 94 | public static SystemUiHider getInstance(Activity activity, View anchorView, int flags) { 95 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 96 | return new SystemUiHiderHoneycomb(activity, anchorView, flags); 97 | } else { 98 | return new SystemUiHiderBase(activity, anchorView, flags); 99 | } 100 | } 101 | 102 | protected SystemUiHider(Activity activity, View anchorView, int flags) { 103 | mActivity = activity; 104 | mAnchorView = anchorView; 105 | mFlags = flags; 106 | } 107 | 108 | /** 109 | * Sets up the system UI hider. Should be called from 110 | * {@link Activity#onCreate}. 111 | */ 112 | public abstract void setup(); 113 | 114 | /** 115 | * Returns whether or not the system UI is visible. 116 | */ 117 | public abstract boolean isVisible(); 118 | 119 | /** 120 | * Hide the system UI. 121 | */ 122 | public abstract void hide(); 123 | 124 | /** 125 | * Show the system UI. 126 | */ 127 | public abstract void show(); 128 | 129 | /** 130 | * Toggle the visibility of the system UI. 131 | */ 132 | public void toggle() { 133 | if (isVisible()) { 134 | hide(); 135 | } else { 136 | show(); 137 | } 138 | } 139 | 140 | /** 141 | * Registers a callback, to be triggered when the system UI visibility 142 | * changes. 143 | */ 144 | public void setOnVisibilityChangeListener(OnVisibilityChangeListener listener) { 145 | if (listener == null) { 146 | listener = sDummyListener; 147 | } 148 | 149 | mOnVisibilityChangeListener = listener; 150 | } 151 | 152 | /** 153 | * A dummy no-op callback for use when there is no other listener set. 154 | */ 155 | private static OnVisibilityChangeListener sDummyListener = new OnVisibilityChangeListener() { 156 | @Override 157 | public void onVisibilityChange(boolean visible) { 158 | } 159 | }; 160 | 161 | /** 162 | * A callback interface used to listen for system UI visibility changes. 163 | */ 164 | public interface OnVisibilityChangeListener { 165 | /** 166 | * Called when the system UI visibility has changed. 167 | * 168 | * @param visible True if the system UI is visible. 169 | */ 170 | public void onVisibilityChange(boolean visible); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /GroovyDroid/src/main/java/me/champeau/groovydroid/util/SystemUiHiderBase.java: -------------------------------------------------------------------------------- 1 | package me.champeau.groovydroid.util; 2 | 3 | import android.app.Activity; 4 | import android.view.View; 5 | import android.view.WindowManager; 6 | 7 | /** 8 | * A base implementation of {@link SystemUiHider}. Uses APIs available in all 9 | * API levels to show and hide the status bar. 10 | */ 11 | public class SystemUiHiderBase extends SystemUiHider { 12 | /** 13 | * Whether or not the system UI is currently visible. This is a cached value 14 | * from calls to {@link #hide()} and {@link #show()}. 15 | */ 16 | private boolean mVisible = true; 17 | 18 | /** 19 | * Constructor not intended to be called by clients. Use 20 | * {@link SystemUiHider#getInstance} to obtain an instance. 21 | */ 22 | protected SystemUiHiderBase(Activity activity, View anchorView, int flags) { 23 | super(activity, anchorView, flags); 24 | } 25 | 26 | @Override 27 | public void setup() { 28 | if ((mFlags & FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES) == 0) { 29 | mActivity.getWindow().setFlags( 30 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 31 | | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, 32 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 33 | | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); 34 | } 35 | } 36 | 37 | @Override 38 | public boolean isVisible() { 39 | return mVisible; 40 | } 41 | 42 | @Override 43 | public void hide() { 44 | if ((mFlags & FLAG_FULLSCREEN) != 0) { 45 | mActivity.getWindow().setFlags( 46 | WindowManager.LayoutParams.FLAG_FULLSCREEN, 47 | WindowManager.LayoutParams.FLAG_FULLSCREEN); 48 | } 49 | mOnVisibilityChangeListener.onVisibilityChange(false); 50 | mVisible = false; 51 | } 52 | 53 | @Override 54 | public void show() { 55 | if ((mFlags & FLAG_FULLSCREEN) != 0) { 56 | mActivity.getWindow().setFlags( 57 | 0, 58 | WindowManager.LayoutParams.FLAG_FULLSCREEN); 59 | } 60 | mOnVisibilityChangeListener.onVisibilityChange(true); 61 | mVisible = true; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /GroovyDroid/src/main/java/me/champeau/groovydroid/util/SystemUiHiderHoneycomb.java: -------------------------------------------------------------------------------- 1 | package me.champeau.groovydroid.util; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.os.Build; 6 | import android.view.View; 7 | import android.view.WindowManager; 8 | 9 | /** 10 | * An API 11+ implementation of {@link SystemUiHider}. Uses APIs available in 11 | * Honeycomb and later (specifically {@link View#setSystemUiVisibility(int)}) to 12 | * show and hide the system UI. 13 | */ 14 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 15 | public class SystemUiHiderHoneycomb extends SystemUiHiderBase { 16 | /** 17 | * Flags for {@link View#setSystemUiVisibility(int)} to use when showing the 18 | * system UI. 19 | */ 20 | private int mShowFlags; 21 | 22 | /** 23 | * Flags for {@link View#setSystemUiVisibility(int)} to use when hiding the 24 | * system UI. 25 | */ 26 | private int mHideFlags; 27 | 28 | /** 29 | * Flags to test against the first parameter in 30 | * {@link android.view.View.OnSystemUiVisibilityChangeListener#onSystemUiVisibilityChange(int)} 31 | * to determine the system UI visibility state. 32 | */ 33 | private int mTestFlags; 34 | 35 | /** 36 | * Whether or not the system UI is currently visible. This is cached from 37 | * {@link android.view.View.OnSystemUiVisibilityChangeListener}. 38 | */ 39 | private boolean mVisible = true; 40 | 41 | /** 42 | * Constructor not intended to be called by clients. Use 43 | * {@link SystemUiHider#getInstance} to obtain an instance. 44 | */ 45 | protected SystemUiHiderHoneycomb(Activity activity, View anchorView, int flags) { 46 | super(activity, anchorView, flags); 47 | 48 | mShowFlags = View.SYSTEM_UI_FLAG_VISIBLE; 49 | mHideFlags = View.SYSTEM_UI_FLAG_LOW_PROFILE; 50 | mTestFlags = View.SYSTEM_UI_FLAG_LOW_PROFILE; 51 | 52 | if ((mFlags & FLAG_FULLSCREEN) != 0) { 53 | // If the client requested fullscreen, add flags relevant to hiding 54 | // the status bar. Note that some of these constants are new as of 55 | // API 16 (Jelly Bean). It is safe to use them, as they are inlined 56 | // at compile-time and do nothing on pre-Jelly Bean devices. 57 | mShowFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; 58 | mHideFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 59 | | View.SYSTEM_UI_FLAG_FULLSCREEN; 60 | } 61 | 62 | if ((mFlags & FLAG_HIDE_NAVIGATION) != 0) { 63 | // If the client requested hiding navigation, add relevant flags. 64 | mShowFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; 65 | mHideFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 66 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; 67 | mTestFlags |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; 68 | } 69 | } 70 | 71 | /** {@inheritDoc} */ 72 | @Override 73 | public void setup() { 74 | mAnchorView.setOnSystemUiVisibilityChangeListener(mSystemUiVisibilityChangeListener); 75 | } 76 | 77 | /** {@inheritDoc} */ 78 | @Override 79 | public void hide() { 80 | mAnchorView.setSystemUiVisibility(mHideFlags); 81 | } 82 | 83 | /** {@inheritDoc} */ 84 | @Override 85 | public void show() { 86 | mAnchorView.setSystemUiVisibility(mShowFlags); 87 | } 88 | 89 | /** {@inheritDoc} */ 90 | @Override 91 | public boolean isVisible() { 92 | return mVisible; 93 | } 94 | 95 | private View.OnSystemUiVisibilityChangeListener mSystemUiVisibilityChangeListener 96 | = new View.OnSystemUiVisibilityChangeListener() { 97 | @Override 98 | public void onSystemUiVisibilityChange(int vis) { 99 | // Test against mTestFlags to see if the system UI is visible. 100 | if ((vis & mTestFlags) != 0) { 101 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { 102 | // Pre-Jelly Bean, we must manually hide the action bar 103 | // and use the old window flags API. 104 | mActivity.getActionBar().hide(); 105 | mActivity.getWindow().setFlags( 106 | WindowManager.LayoutParams.FLAG_FULLSCREEN, 107 | WindowManager.LayoutParams.FLAG_FULLSCREEN); 108 | } 109 | 110 | // Trigger the registered listener and cache the visibility 111 | // state. 112 | mOnVisibilityChangeListener.onVisibilityChange(false); 113 | mVisible = false; 114 | 115 | } else { 116 | mAnchorView.setSystemUiVisibility(mShowFlags); 117 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { 118 | // Pre-Jelly Bean, we must manually show the action bar 119 | // and use the old window flags API. 120 | mActivity.getActionBar().show(); 121 | mActivity.getWindow().setFlags( 122 | 0, 123 | WindowManager.LayoutParams.FLAG_FULLSCREEN); 124 | } 125 | 126 | // Trigger the registered listener and cache the visibility 127 | // state. 128 | mOnVisibilityChangeListener.onVisibilityChange(true); 129 | mVisible = true; 130 | } 131 | } 132 | }; 133 | } 134 | -------------------------------------------------------------------------------- /GroovyDroid/src/main/res/drawable-hdpi/groovy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/melix/grooidshell-example/ee781ba1a07fb9ae60304236c51f93a66232c7c6/GroovyDroid/src/main/res/drawable-hdpi/groovy.png -------------------------------------------------------------------------------- /GroovyDroid/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/melix/grooidshell-example/ee781ba1a07fb9ae60304236c51f93a66232c7c6/GroovyDroid/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /GroovyDroid/src/main/res/drawable-mdpi/groovy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/melix/grooidshell-example/ee781ba1a07fb9ae60304236c51f93a66232c7c6/GroovyDroid/src/main/res/drawable-mdpi/groovy.png -------------------------------------------------------------------------------- /GroovyDroid/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/melix/grooidshell-example/ee781ba1a07fb9ae60304236c51f93a66232c7c6/GroovyDroid/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /GroovyDroid/src/main/res/drawable-xhdpi/groovy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/melix/grooidshell-example/ee781ba1a07fb9ae60304236c51f93a66232c7c6/GroovyDroid/src/main/res/drawable-xhdpi/groovy.png -------------------------------------------------------------------------------- /GroovyDroid/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/melix/grooidshell-example/ee781ba1a07fb9ae60304236c51f93a66232c7c6/GroovyDroid/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /GroovyDroid/src/main/res/drawable-xxhdpi/groovy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/melix/grooidshell-example/ee781ba1a07fb9ae60304236c51f93a66232c7c6/GroovyDroid/src/main/res/drawable-xxhdpi/groovy.png -------------------------------------------------------------------------------- /GroovyDroid/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/melix/grooidshell-example/ee781ba1a07fb9ae60304236c51f93a66232c7c6/GroovyDroid/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /GroovyDroid/src/main/res/layout/groovy_main.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | 25 | 26 | 34 | 35 | 48 | 49 |