├── .gitignore ├── LICENSE ├── README.md ├── api ├── build.gradle ├── javadoc-scripts │ ├── .gitignore │ ├── generate_javadoc.sh │ ├── index.html │ ├── javadoc_stylesheet.css │ ├── prettify.css │ ├── prettify.js │ └── tweak_javadoc_html.py └── src │ └── main │ └── java │ └── com │ └── actionlauncher │ └── api │ ├── LiveWallpaperInfo.java │ ├── LiveWallpaperSource.java │ ├── actionpalette │ ├── ActionPalette.java │ ├── AsyncTaskCompat.java │ ├── AsyncTaskCompatHoneycomb.java │ ├── ColorCutQuantizer.java │ ├── ColorHistogram.java │ ├── ColorUtils.java │ └── DefaultGenerator.java │ └── internal │ ├── ProtocolConstants.java │ └── SourceState.java ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── keystore.properties.example ├── local.properties.example ├── main ├── build.gradle ├── proguard-project.txt ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ ├── Alegreya-BlackItalic.ttf │ │ ├── Alegreya-Italic.ttf │ │ ├── AlegreyaModifiedReadme │ │ ├── kepler-01.jpg │ │ ├── kepler-02.jpg │ │ ├── kepler-03.jpg │ │ └── starrynight.jpg │ │ ├── java │ │ ├── com │ │ │ └── google │ │ │ │ └── android │ │ │ │ └── apps │ │ │ │ └── muzei │ │ │ │ ├── ArtDetailViewport.java │ │ │ │ ├── LockScreenVisibleReceiver.java │ │ │ │ ├── MuzeiActivity.java │ │ │ │ ├── MuzeiWallpaperService.java │ │ │ │ ├── event │ │ │ │ ├── ArtDetailOpenedClosedEvent.java │ │ │ │ ├── ArtworkSizeChangedEvent.java │ │ │ │ ├── LockScreenVisibleChangedEvent.java │ │ │ │ ├── SwitchingPhotosStateChangedEvent.java │ │ │ │ ├── WallpaperActiveStateChangedEvent.java │ │ │ │ └── WallpaperSizeChangedEvent.java │ │ │ │ ├── render │ │ │ │ ├── BitmapRegionLoader.java │ │ │ │ ├── GLColorOverlay.java │ │ │ │ ├── GLPicture.java │ │ │ │ ├── GLTextureView.java │ │ │ │ ├── GLUtil.java │ │ │ │ ├── ImageUtil.java │ │ │ │ ├── LocalRenderController.java │ │ │ │ ├── MuzeiBlurRenderer.java │ │ │ │ ├── MuzeiRendererFragment.java │ │ │ │ └── RenderController.java │ │ │ │ ├── settings │ │ │ │ └── Prefs.java │ │ │ │ └── util │ │ │ │ ├── CheatSheet.java │ │ │ │ ├── DrawInsetsFrameLayout.java │ │ │ │ ├── IOUtil.java │ │ │ │ ├── ImageBlurrer.java │ │ │ │ ├── LogUtil.java │ │ │ │ ├── LogoPaths.java │ │ │ │ ├── MathUtil.java │ │ │ │ ├── MultiSelectionController.java │ │ │ │ ├── ObservableHorizontalScrollView.java │ │ │ │ ├── PanScaleProxyView.java │ │ │ │ ├── ScrimUtil.java │ │ │ │ ├── Scrollbar.java │ │ │ │ ├── SelectionBuilder.java │ │ │ │ ├── ShadowDipsTextView.java │ │ │ │ ├── SvgPathParser.java │ │ │ │ ├── TickingFloatAnimator.java │ │ │ │ ├── TypefaceUtil.java │ │ │ │ └── Zoomer.java │ │ └── net │ │ │ └── rbgrn │ │ │ └── android │ │ │ └── glwallpaperservice │ │ │ ├── BaseConfigChooser.java │ │ │ └── GLWallpaperService.java │ │ └── res │ │ ├── animator │ │ ├── fade_in.xml │ │ └── fade_out.xml │ │ ├── drawable-hdpi │ │ ├── ic_notif_full_info.png │ │ ├── ic_notif_full_next_artwork.png │ │ └── ic_notif_full_user_command.png │ │ ├── drawable-nodpi │ │ ├── ic_source_featured.png │ │ ├── ic_source_gallery.png │ │ ├── ic_source_selected.png │ │ └── thumb.png │ │ ├── drawable-v21 │ │ ├── grey_selectable_item_background_circle.xml │ │ ├── settings_source_item_image_overlay.xml │ │ └── white_selectable_item_background.xml │ │ ├── drawable-xhdpi │ │ └── ic_stat_muzei.png │ │ ├── drawable-xxhdpi │ │ ├── gallery_settings_chosen_photo_selected.png │ │ ├── grumpy_mcpuzzles.png │ │ ├── ic_ab_done.png │ │ ├── ic_ab_up.png │ │ ├── ic_action_play.png │ │ ├── ic_action_remove.png │ │ ├── ic_action_rotate_interval.png │ │ ├── ic_add_photos.png │ │ ├── ic_notif_info.png │ │ ├── ic_notif_next_artwork.png │ │ ├── ic_overflow.png │ │ ├── ic_skip.png │ │ ├── ic_source_settings.png │ │ ├── ic_stat_muzei.png │ │ ├── logo_subtitle.png │ │ ├── scrubber_control_disabled.png │ │ ├── scrubber_control_focused.png │ │ ├── scrubber_control_normal.png │ │ ├── scrubber_control_pressed.png │ │ ├── scrubber_primary.9.png │ │ ├── scrubber_secondary.9.png │ │ ├── scrubber_track.9.png │ │ ├── scrubber_track_blur_amount.9.png │ │ ├── scrubber_track_dim_amount.9.png │ │ ├── scrubber_track_grey_amount.9.png │ │ ├── spinner_triangle.png │ │ ├── tutorial_icon_off.png │ │ ├── tutorial_icon_on.png │ │ ├── white_item_focused.9.png │ │ └── white_item_pressed.9.png │ │ ├── drawable │ │ ├── grey_selectable_item_background_circle.xml │ │ ├── intro_background_protection.xml │ │ ├── metadata_scrim.xml │ │ ├── popup_background.xml │ │ ├── scrubber_control_selector.xml │ │ ├── scrubber_progress_blur_amount.xml │ │ ├── scrubber_progress_dim_amount.xml │ │ ├── scrubber_progress_grey_amount.xml │ │ ├── scrubber_progress_horizontal.xml │ │ ├── settings_source_item_image_overlay.xml │ │ ├── statusbar_scrim.xml │ │ ├── tutorial_icon.xml │ │ ├── white_circle_button.xml │ │ └── white_selectable_item_background.xml │ │ ├── layout │ │ └── muzei_activity.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── values-sw360dp │ │ └── dimens.xml │ │ ├── values-sw600dp │ │ └── dimens.xml │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ids.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── wallpaper.xml └── version.properties ├── screenshot.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | build/ 16 | .gradle/ 17 | 18 | # Project files 19 | *.iml 20 | .idea 21 | # .idea/workspace.xml 22 | 23 | # Local configuration file (sdk path, etc) 24 | local.properties 25 | keystore.properties 26 | 27 | # Windows thumbnail db 28 | .DS_Store 29 | 30 | # Idea non-crucial project fileS 31 | *.iws 32 | 33 | # Sandbox stuff 34 | _sandbox 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Action Launcher API 2 | ================================== 3 | 4 | A simple API live wallpaper developers can use to allow [Action Launcher 3](2) to theme itself based on the current colors of your wallpaper. 5 | 6 | 7 | 8 | Note: use of this API is only necessary for **live wallpapers** (not status wallpapers). This is due to Android not providing any APIs for apps to fetch Bitmap data about the current live wallpaper. 9 | 10 | 11 | 12 | Usage 13 | ===== 14 | 15 | 1. Integrate the Action 3 API code into the `dependencies` section of your `build.gradle` file: 16 | 17 | `compile 'com.actionlauncher:action3-api:1.+'` 18 |

If you're not using Android Studio/gradle, you can add the [`action3-api.jar`][5] to your `/libs` folder, or copy the API [code][3] directly into your project.

19 | 20 | 21 | 2. Add this code to your `AndroidManifest.xml` (inside the `Application` entry): 22 | 23 | ``` 24 | 27 | 28 | 29 | 30 | 31 | ``` 32 | 3. In your application code, you will need a `Bitmap` instance of your live wallpaper. At the point in your code where you have this `Bitmap` instance, add the following: 33 | 34 | ``` 35 | Bitmap myBitmap = ... 36 | try { 37 | LiveWallpaperSource.with(context) 38 | .setBitmapSynchronous(myBitmap) 39 | .run(); 40 | } catch (OutOfMemoryError outOfMemoryError) { 41 | // Palette generation was unable to process the Bitmap passed in to 42 | // setBitmapSynchronous(). Consider using a smaller image. 43 | // See ActionPalette.DEFAULT_RESIZE_BITMAP_MAX_DIMENSION 44 | } catch (IllegalArgumentException illegalArgumentEx) { 45 | // Raised during palette generation. Check your Bitmap. 46 | } catch (IllegalStateException illegalStateException) { 47 | // Raised during palette generation. Check your Bitmap. 48 | } 49 | ``` 50 | 51 | 52 | 53 |
To test it all works: 54 | 55 | * Load Action Launcher 3 (you *must* be using version 3.3 or later). 56 | * Ensure your wallpaper is set as the live wallpaper. 57 | * Ensure Action Launcher's wallpaper extraction mode is enabled (Settings -> Quicktheme -> Theme -> Wallpaper). 58 | * As you're integrating the API, be sure to turn on Settings -> Help -> Advanced -> Live wallpaper API debug in Action Launcher 3. By doing so, you will enable a debug mode where pressing the voice search button on the search bar will trigger a request to your app for the latest `LiveWallpaperInfo` data. 59 | 60 | 61 | Demo 62 | ==== 63 | The `main` app in this repository demonstrates the live wallpaper functionality. It is basically the main app from the [Android Live Wallpaper Hello World project](1). If you double-tap empty space on Action Launcher 3's home screen, the wallpaper image will change, and you items such as the search bar will have their colors updated as per the current wallpaper image in Action Launcher 3. 64 | 65 | Check out the `LiveWallpaperSource.with()` call in `MuzeiBlurRenderer.java`. 66 | 67 | Notes 68 | ===== 69 | 70 | * Keep in mind that each time you call `LiveWallpaperSource.setBitmapSynchronous()`, a new palette will be generated. In order to not waste battery, you only want to make this call when you know there has been a meaninful visual change in your wallpaper app and Action Launcher's Quicktheme feature should be updated. 71 | * This API includes a copy of API 22's Palette library from Support Library named `ActionPalette`[4]. It has been integrated directly into the ActionLauncherApi rather than as a dependency because: 72 | * Many live-wallpaper developers are still using Eclipse, which has seemingly isn't well set up to use AARs. 73 | * Makes the dependencies easier. 74 | * It doesn't take much code size, so there's little harm in it. 75 | 76 | 77 | 3rd party examples 78 | ================== 79 | The following Android apps make use of this API: 80 | 81 | * [Minima Pro Live Wallpaper](https://play.google.com/store/apps/details?id=com.joko.minimapro) 82 | * [TapDeck - Wallpaper Discovery](https://play.google.com/store/apps/details?id=io.tapdeck.android) 83 | 84 | 85 | License 86 | ======= 87 | 88 | Copyright 2015 Chris Lacy 89 | 90 | Licensed under the Apache License, Version 2.0 (the "License"); 91 | you may not use this file except in compliance with the License. 92 | You may obtain a copy of the License at 93 | 94 | http://www.apache.org/licenses/LICENSE-2.0 95 | 96 | Unless required by applicable law or agreed to in writing, software 97 | distributed under the License is distributed on an "AS IS" BASIS, 98 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 99 | See the License for the specific language governing permissions and 100 | limitations under the License. 101 | 102 | [1]: https://github.com/chrislacy/AndroidLiveWallpaperHelloWorld 103 | [2]: https://play.google.com/store/apps/details?id=com.actionlauncher.playstore 104 | [3]: https://github.com/chrislacy/ActionLauncherApi/tree/master/api/src/main/java 105 | [4]: https://github.com/chrislacy/ActionLauncherApi/tree/master/api/src/main/java/com/actionlauncher/api/actionpalette 106 | [5]: https://oss.sonatype.org/content/repositories/releases/com/actionlauncher/action3-api/1.1.0/action3-api-1.1.0.jar 107 | -------------------------------------------------------------------------------- /api/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | apply plugin: 'maven' 18 | apply plugin: 'signing' 19 | apply plugin: 'java' 20 | sourceCompatibility = 1.7 21 | targetCompatibility = 1.7 22 | 23 | jar.baseName = 'action3-api' 24 | group = 'com.actionlauncher' 25 | version = '1.1.0' 26 | 27 | def Properties props = new Properties() 28 | props.load(new FileInputStream(file('../local.properties'))) 29 | 30 | def android = [ 31 | sdk: props["sdk.dir"], 32 | target: 'android-21' 33 | ] 34 | 35 | allprojects { ext."signing.keyId" = props["signing.keyId"] } 36 | allprojects { ext."signing.password" = props["signing.password"] } 37 | allprojects { ext."signing.secretKeyRingFile" = props["signing.secretKeyRingFile"] } 38 | 39 | 40 | sourceSets { 41 | main { 42 | java { 43 | srcDir 'build/source/aidl/debug' 44 | } 45 | } 46 | } 47 | 48 | task javadoc(type: Exec, overwrite: true, dependsOn: 'jar') { 49 | commandLine './javadoc-scripts/generate_javadoc.sh' 50 | } 51 | 52 | task javadocJar(type: Jar, dependsOn: javadoc) { 53 | classifier = 'javadoc' 54 | baseName = jar.baseName 55 | from 'build/javadoc' 56 | } 57 | 58 | task sourcesJar(type: Jar, dependsOn: classes) { 59 | classifier = 'sources' 60 | baseName = jar.baseName 61 | from sourceSets.main.allSource 62 | } 63 | 64 | artifacts { 65 | archives jar 66 | archives sourcesJar 67 | archives javadocJar 68 | } 69 | 70 | dependencies { 71 | compile files("${android.sdk}/platforms/${android.target}/android.jar") 72 | } 73 | 74 | // http://jedicoder.blogspot.com/2011/11/automated-gradle-project-deployment-to.html 75 | // TODO: switch to http://www.gradle.org/docs/current/userguide/publishing_maven.html 76 | 77 | signing { 78 | sign configurations.archives 79 | } 80 | 81 | uploadArchives { 82 | repositories { 83 | mavenDeployer { 84 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 85 | 86 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { 87 | authentication(userName: props["sonatypeUsername"], password: props["sonatypePassword"]) 88 | } 89 | 90 | snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { 91 | authentication(userName: props["sonatypeUsername"], password: props["sonatypePassword"]) 92 | } 93 | 94 | pom.project { 95 | name 'ActionLauncherApi' 96 | packaging 'jar' 97 | description 'The Action Launcher API allows live wallpaper app developers to provide palette details about your wallpaper so Action Launcher might theme items on a user\'s Home screen with the current colors from their wallpaper.' 98 | url 'https://github.com/chrislacy/ActionLauncherApi' 99 | 100 | scm { 101 | url 'https://github.com/chrislacy/ActionLauncherApi.git' 102 | connection 'scm:git:https://github.com/chrislacy/ActionLauncherApi' 103 | developerConnection 'scm:git:https://github.com/chrislacy/ActionLauncherApi' 104 | } 105 | 106 | licenses { 107 | license { 108 | name 'The Apache Software License, Version 2.0' 109 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 110 | distribution 'repo' 111 | } 112 | } 113 | 114 | developers { 115 | developer { 116 | id 'chrislacy' 117 | name 'Chris Lacy' 118 | } 119 | } 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /api/javadoc-scripts/.gitignore: -------------------------------------------------------------------------------- 1 | _locals.sh 2 | -------------------------------------------------------------------------------- /api/javadoc-scripts/generate_javadoc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2014 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | PLATFORM=android-17 17 | OUT_PATH=../build/javadoc 18 | 19 | cd `dirname $0` 20 | 21 | #source _locals.sh 22 | javadoc -linkoffline http://developer.android.com/reference ${ANDROID_SDK}/docs/reference \ 23 | -sourcepath ../src/main/java:../build/source/aidl/debug \ 24 | -classpath ${ANDROID_SDK}/platforms/${PLATFORM}/android.jar:${ANDROID_SDK}/tools/support/annotations.jar \ 25 | -d ${OUT_PATH} \ 26 | -notree -nonavbar -noindex -notree -nohelp -nodeprecated \ 27 | -stylesheetfile javadoc_stylesheet.css \ 28 | -windowtitle "Action Launcher API" \ 29 | -doctitle "Action Launcher API" \ 30 | com.actionlauncher.api 31 | 32 | cp prettify* ${OUT_PATH}/resources/ 33 | 34 | python tweak_javadoc_html.py ${OUT_PATH}/ 35 | -------------------------------------------------------------------------------- /api/javadoc-scripts/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | 22 | 23 | Redirecting you to the 24 | com.actionlauncher.api 25 | package summary… 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /api/javadoc-scripts/javadoc_stylesheet.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* Javadoc style sheet */ 18 | 19 | /* Define colors, fonts and other style attributes here to override the defaults */ 20 | 21 | /* Page background color */ 22 | body { 23 | background-color: #fff; 24 | color: #333; 25 | margin: 60px 80px; 26 | font-family: roboto; 27 | font-size: 16px; 28 | line-height: 140%; 29 | } 30 | 31 | /* Headings */ 32 | h2 { 33 | font-size: 200%; 34 | font-weight: 300; 35 | } 36 | 37 | /* Table colors */ 38 | /*.TableHeadingColor { background: #CCCCFF; color:#000000 } 39 | .TableSubHeadingColor { background: #EEEEFF; color:#000000 } 40 | .TableRowColor { background: #FFFFFF; color:#000000 }*/ 41 | 42 | table, td, tr, th { 43 | border:0; 44 | } 45 | 46 | hr { 47 | margin: 20px 0; 48 | border: 1px solid #09c; 49 | } 50 | 51 | th { 52 | background-color: #eee; 53 | border-bottom: 1px solid #ccc; 54 | } 55 | 56 | td { 57 | padding: 6px; 58 | } 59 | 60 | table { 61 | margin-top: 20px; 62 | } 63 | 64 | td { 65 | border-bottom: 1px solid #eee; 66 | } 67 | 68 | pre, code { 69 | font-family: inconsolata; 70 | font-size: 110%; 71 | } 72 | 73 | code { 74 | color: #096; 75 | } 76 | 77 | pre.prettyprint { 78 | background: #eee; 79 | border: 1px solid #ccc; 80 | border-radius: 4px; 81 | padding: 20px 10px; 82 | } 83 | 84 | a, a code { 85 | color: #09c; 86 | text-decoration: none; 87 | } 88 | 89 | a { 90 | border-bottom: 1px dotted #09c; 91 | } 92 | 93 | a:hover { 94 | border-bottom: 1px solid #09c; 95 | } 96 | -------------------------------------------------------------------------------- /api/javadoc-scripts/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} -------------------------------------------------------------------------------- /api/javadoc-scripts/tweak_javadoc_html.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2014 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import re 17 | import os 18 | import sys 19 | import shutil 20 | 21 | 22 | PACKAGE_NAME = 'com.actionlauncher.api' 23 | 24 | 25 | def main(): 26 | root = sys.argv[1] 27 | for path, _, files in os.walk(root): 28 | for f in [f for f in files if f.endswith('.html')]: 29 | fp = open(os.path.join(path, f), 'r') 30 | html = fp.read() 31 | fp.close() 32 | 33 | toroot = '.' 34 | if path.startswith(root): 35 | subpath = path[len(root):] 36 | toroot = '../' * (subpath.count('/') + 1) 37 | 38 | html = process(toroot, html) 39 | if f.endswith('package-summary.html'): 40 | html = process_package_summary(toroot, html) 41 | 42 | fp = open(os.path.join(path, f), 'w') 43 | fp.write(html) 44 | fp.close() 45 | 46 | shutil.copy('index.html', root) 47 | 48 | 49 | def process(toroot, html): 50 | re_flags = re.I | re.M | re.S 51 | html = re.sub(r'
\s+
', '', html, 0, re_flags) 52 | html = re.sub(r'windowTitle\(\);', 'windowTitle();prettyPrint();', html, 0, re_flags) 53 | html = re.sub(r'\s+', '', html, 0, re_flags) 54 | html = re.sub(PACKAGE_NAME + '', '' + PACKAGE_NAME + '', html, 0, re_flags) 55 | html = re.sub(r'', ''' 56 | 57 | 58 | 59 | ''' % dict(root=toroot), html, 0, re_flags) 60 | #html = re.sub(r'
\s+
', '', html, re.I | re.M | re.S) 61 | return html 62 | 63 | 64 | def process_package_summary(toroot, html): 65 | re_flags = re.I | re.M | re.S 66 | #html = re.sub(r'\s+.*?\n', '\n', html, 0, re_flags) 67 | html = re.sub(r'See:\n
', '\n', html, 0, re_flags) 68 | html = re.sub(r'  ( )+[^\n]+\n', '\n', html, 0, re_flags) 69 | html = re.sub(r'\n[^\n]+\s+description\n', '\nDescription\n', html, 0, re_flags) 70 | return html 71 | 72 | 73 | if __name__ == '__main__': 74 | main() 75 | -------------------------------------------------------------------------------- /api/src/main/java/com/actionlauncher/api/actionpalette/AsyncTaskCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.actionlauncher.api.actionpalette; 18 | 19 | import android.os.AsyncTask; 20 | import android.os.Build; 21 | 22 | /** 23 | * Helper for accessing features in {@link android.os.AsyncTask} 24 | * introduced after API level 4 in a backwards compatible fashion. 25 | */ 26 | class AsyncTaskCompat { 27 | 28 | /** 29 | * Executes the task with the specified parameters, allowing multiple tasks to run in parallel 30 | * on a pool of threads managed by {@link android.os.AsyncTask}. 31 | * 32 | * @param task The {@link android.os.AsyncTask} to execute. 33 | * @param params The parameters of the task. 34 | * @return the instance of AsyncTask. 35 | */ 36 | public static AsyncTask executeParallel( 37 | AsyncTask task, 38 | Params... params) { 39 | if (task == null) { 40 | throw new IllegalArgumentException("task can not be null"); 41 | } 42 | 43 | if (Build.VERSION.SDK_INT >= 11) { 44 | // From API 11 onwards, we need to manually select the THREAD_POOL_EXECUTOR 45 | AsyncTaskCompatHoneycomb.executeParallel(task, params); 46 | } else { 47 | // Before API 11, all tasks were run in parallel 48 | task.execute(params); 49 | } 50 | 51 | return task; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /api/src/main/java/com/actionlauncher/api/actionpalette/AsyncTaskCompatHoneycomb.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.actionlauncher.api.actionpalette; 18 | 19 | import android.os.AsyncTask; 20 | 21 | /** 22 | * Implementation of AsyncTask compatibility that can call Honeycomb APIs. 23 | */ 24 | class AsyncTaskCompatHoneycomb { 25 | 26 | static void executeParallel( 27 | AsyncTask task, 28 | Params... params) { 29 | task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /api/src/main/java/com/actionlauncher/api/actionpalette/ColorHistogram.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.actionlauncher.api.actionpalette; 18 | 19 | import java.util.Arrays; 20 | 21 | /** 22 | * Class which provides a histogram for RGB values. 23 | */ 24 | final class ColorHistogram { 25 | 26 | private final int[] mColors; 27 | private final int[] mColorCounts; 28 | private final int mNumberColors; 29 | 30 | /** 31 | * A new {@link ColorHistogram} instance. 32 | * 33 | * @param pixels array of image contents 34 | */ 35 | ColorHistogram(final int[] pixels) { 36 | // Sort the pixels to enable counting below 37 | Arrays.sort(pixels); 38 | 39 | // Count number of distinct colors 40 | mNumberColors = countDistinctColors(pixels); 41 | 42 | // Create arrays 43 | mColors = new int[mNumberColors]; 44 | mColorCounts = new int[mNumberColors]; 45 | 46 | // Finally count the frequency of each color 47 | countFrequencies(pixels); 48 | } 49 | 50 | /** 51 | * @return number of distinct colors in the image. 52 | */ 53 | int getNumberOfColors() { 54 | return mNumberColors; 55 | } 56 | 57 | /** 58 | * @return an array containing all of the distinct colors in the image. 59 | */ 60 | int[] getColors() { 61 | return mColors; 62 | } 63 | 64 | /** 65 | * @return an array containing the frequency of a distinct colors within the image. 66 | */ 67 | int[] getColorCounts() { 68 | return mColorCounts; 69 | } 70 | 71 | private static int countDistinctColors(final int[] pixels) { 72 | if (pixels.length < 2) { 73 | // If we have less than 2 pixels we can stop here 74 | return pixels.length; 75 | } 76 | 77 | // If we have at least 2 pixels, we have a minimum of 1 color... 78 | int colorCount = 1; 79 | int currentColor = pixels[0]; 80 | 81 | // Now iterate from the second pixel to the end, counting distinct colors 82 | for (int i = 1; i < pixels.length; i++) { 83 | // If we encounter a new color, increase the population 84 | if (pixels[i] != currentColor) { 85 | currentColor = pixels[i]; 86 | colorCount++; 87 | } 88 | } 89 | 90 | return colorCount; 91 | } 92 | 93 | private void countFrequencies(final int[] pixels) { 94 | if (pixels.length == 0) { 95 | return; 96 | } 97 | 98 | int currentColorIndex = 0; 99 | int currentColor = pixels[0]; 100 | 101 | mColors[currentColorIndex] = currentColor; 102 | mColorCounts[currentColorIndex] = 1; 103 | 104 | if (pixels.length == 1) { 105 | // If we only have one pixel, we can stop here 106 | return; 107 | } 108 | 109 | // Now iterate from the second pixel to the end, population distinct colors 110 | for (int i = 1; i < pixels.length; i++) { 111 | if (pixels[i] == currentColor) { 112 | // We've hit the same color as before, increase population 113 | mColorCounts[currentColorIndex]++; 114 | } else { 115 | // We've hit a new color, increase index 116 | currentColor = pixels[i]; 117 | 118 | currentColorIndex++; 119 | mColors[currentColorIndex] = currentColor; 120 | mColorCounts[currentColorIndex] = 1; 121 | } 122 | } 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /api/src/main/java/com/actionlauncher/api/internal/ProtocolConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.actionlauncher.api.internal; 18 | 19 | /** 20 | * Internal intent constants for sources. 21 | */ 22 | public class ProtocolConstants { 23 | // Received intents 24 | public static final String ACTION_SUBSCRIBE = "com.actionlauncher.api.action.SUBSCRIBE"; 25 | public static final String EXTRA_SUBSCRIBER_COMPONENT = "com.actionlauncher.api.extra.SUBSCRIBER_COMPONENT"; 26 | public static final String EXTRA_TOKEN = "com.actionlauncher.api.extra.TOKEN"; 27 | public static final String EXTRA_STATE = "com.actionlauncher.api.extra.STATE"; 28 | 29 | public static final String ACTION_FETCH_PALETTE = "com.actionlauncher.api.FETCH_PALETTE"; 30 | 31 | // Sent intents 32 | public static final String ACTION_PUBLISH_STATE = "com.actionlauncher.api.action.PUBLISH_UPDATE"; 33 | public static final String EXTRA_LIVE_WALLPAPER_INFO = "com.actionlauncher.api.extra.LIVE_WALLPAPER_INFO"; 34 | 35 | private ProtocolConstants() { 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /api/src/main/java/com/actionlauncher/api/internal/SourceState.java: -------------------------------------------------------------------------------- 1 | package com.actionlauncher.api.internal; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.actionlauncher.api.LiveWallpaperInfo; 6 | 7 | import org.json.JSONException; 8 | import org.json.JSONObject; 9 | 10 | /** 11 | * Represents the published state of a live wallpaper source. 12 | */ 13 | public class SourceState { 14 | private static final String KEY_CURRENT_LIVE_WALLPAPER_INFO = "currentLWPI"; 15 | 16 | private LiveWallpaperInfo mCurrentLiveWallpaperInfo; 17 | 18 | public LiveWallpaperInfo getCurrentLiveWallpaperInfo() { 19 | return mCurrentLiveWallpaperInfo; 20 | } 21 | 22 | public void setCurrentLiveWallpaperInfo(LiveWallpaperInfo liveWallpaperInfo) { 23 | mCurrentLiveWallpaperInfo = liveWallpaperInfo; 24 | } 25 | 26 | public Bundle toBundle() { 27 | Bundle bundle = new Bundle(); 28 | if (mCurrentLiveWallpaperInfo != null) { 29 | bundle.putBundle(KEY_CURRENT_LIVE_WALLPAPER_INFO, mCurrentLiveWallpaperInfo.toBundle()); 30 | } 31 | return bundle; 32 | } 33 | 34 | public static SourceState fromBundle(Bundle bundle) { 35 | SourceState state = new SourceState(); 36 | Bundle liveWallpaperInfoBundle = bundle.getBundle(KEY_CURRENT_LIVE_WALLPAPER_INFO); 37 | if (liveWallpaperInfoBundle != null) { 38 | state.mCurrentLiveWallpaperInfo = LiveWallpaperInfo.fromBundle(liveWallpaperInfoBundle); 39 | } 40 | return state; 41 | } 42 | 43 | public JSONObject toJson() throws JSONException{ 44 | JSONObject jsonObject = new JSONObject(); 45 | if (mCurrentLiveWallpaperInfo != null) { 46 | jsonObject.put(KEY_CURRENT_LIVE_WALLPAPER_INFO, mCurrentLiveWallpaperInfo.toJson()); 47 | } 48 | return jsonObject; 49 | } 50 | 51 | public void readJson(JSONObject jsonObject) throws JSONException { 52 | JSONObject liveWallpaperInfoObject = jsonObject.optJSONObject(KEY_CURRENT_LIVE_WALLPAPER_INFO); 53 | if (liveWallpaperInfoObject != null) { 54 | mCurrentLiveWallpaperInfo = LiveWallpaperInfo.fromJson(liveWallpaperInfoObject); 55 | } 56 | } 57 | 58 | public static SourceState fromJson(JSONObject jsonObject) throws JSONException{ 59 | SourceState state = new SourceState(); 60 | state.readJson(jsonObject); 61 | return state; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ext { 18 | gradleClasspath = 'com.android.tools.build:gradle:2.2.0' 19 | compileSdkVersion = 21 20 | buildToolsVersion = "25.0.1" 21 | targetSdkVersion = compileSdkVersion 22 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Dec 02 08:32:54 AEST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /keystore.properties.example: -------------------------------------------------------------------------------- 1 | # This is an example keystore.properties file. 2 | store=/path/to/your.keystore 3 | alias=your_alias 4 | pass=your_password 5 | storePass=your_keystore_password 6 | -------------------------------------------------------------------------------- /local.properties.example: -------------------------------------------------------------------------------- 1 | # This is an example local.properties file. 2 | sdk.dir=/path/to/android/sdk 3 | keystore.props.file=../keystore.properties 4 | -------------------------------------------------------------------------------- /main/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | buildscript { 19 | repositories { 20 | mavenCentral() 21 | } 22 | dependencies { 23 | classpath rootProject.ext.gradleClasspath 24 | } 25 | } 26 | 27 | apply plugin: 'com.android.application' 28 | 29 | project.archivesBaseName = "muzei" 30 | 31 | repositories { 32 | mavenCentral() 33 | } 34 | 35 | android { 36 | compileSdkVersion rootProject.ext.compileSdkVersion 37 | buildToolsVersion rootProject.ext.buildToolsVersion 38 | 39 | def Properties versionProps = new Properties() 40 | versionProps.load(new FileInputStream(file('version.properties'))) 41 | 42 | defaultConfig { 43 | minSdkVersion 17 44 | targetSdkVersion rootProject.ext.targetSdkVersion 45 | renderscriptTargetApi rootProject.ext.targetSdkVersion 46 | renderscriptSupportModeEnabled true 47 | 48 | applicationId 'com.example.actionlauncher.api.livewallpaperdemo' 49 | 50 | versionName versionProps['name'] 51 | versionCode versionProps['code'].toInteger() 52 | } 53 | 54 | signingConfigs { 55 | release { 56 | def Properties localProps = new Properties() 57 | localProps.load(new FileInputStream(file('../local.properties'))) 58 | def Properties keyProps = new Properties() 59 | if (localProps['keystore.props.file'] != null) { 60 | keyProps.load(new FileInputStream(file(localProps['keystore.props.file']))) 61 | } 62 | storeFile keyProps["store"] != null ? file(keyProps["store"]) : null 63 | keyAlias keyProps["alias"] ?: "" 64 | storePassword keyProps["storePass"] ?: "" 65 | keyPassword keyProps["pass"] ?: "" 66 | } 67 | } 68 | 69 | productFlavors { 70 | dev { 71 | // dev utilizes minSDKVersion = 21 to allow the Android gradle plugin 72 | // to pre-dex each module and produce an APK that can be tested on 73 | // Android Lollipop without time consuming dex merging processes. 74 | //minSdkVersion 21 75 | //multiDexEnabled true 76 | } 77 | 78 | prod { 79 | } 80 | } 81 | 82 | buildTypes { 83 | debug { 84 | versionNameSuffix " Debug" 85 | } 86 | 87 | release { 88 | minifyEnabled true 89 | shrinkResources true 90 | proguardFiles getDefaultProguardFile('proguard-android.txt'), file('proguard-project.txt') 91 | signingConfig signingConfigs.release 92 | } 93 | 94 | publicBeta.initWith(buildTypes.release) 95 | publicBeta { 96 | minifyEnabled true 97 | shrinkResources true 98 | proguardFiles getDefaultProguardFile('proguard-android.txt'), file('proguard-project.txt') 99 | versionNameSuffix " " + versionProps['betaNumber'] 100 | } 101 | 102 | publicDebug.initWith(buildTypes.publicBeta) 103 | publicDebug { 104 | debuggable true 105 | renderscriptDebuggable true 106 | minifyEnabled true 107 | shrinkResources true 108 | proguardFiles getDefaultProguardFile('proguard-android.txt'), file('proguard-project.txt') 109 | versionNameSuffix " Debug " + versionProps['betaNumber'] 110 | } 111 | } 112 | 113 | compileOptions { 114 | sourceCompatibility JavaVersion.VERSION_1_7 115 | targetCompatibility JavaVersion.VERSION_1_7 116 | } 117 | } 118 | 119 | dependencies { 120 | compile 'com.squareup.okhttp:okhttp:2.1.0' 121 | compile 'com.squareup.okhttp:okhttp-urlconnection:2.1.0' 122 | compile 'com.squareup.picasso:picasso:2.4.0' 123 | compile 'de.greenrobot:eventbus:2.4.0' 124 | compile 'com.android.support:appcompat-v7:21.0.3' 125 | compile 'com.android.support:recyclerview-v7:21.0.2' 126 | compile project(':api') 127 | } 128 | -------------------------------------------------------------------------------- /main/proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | 22 | -keep public class * extends android.app,backup.BackupAgent 23 | 24 | -keepclassmembers class ** { 25 | public void onEvent*(**); 26 | } 27 | 28 | -dontobfuscate 29 | 30 | # okhttp 31 | 32 | -dontwarn com.squareup.okhttp.** 33 | 34 | # okio 35 | 36 | -dontwarn okio.** 37 | 38 | # Renderscript support 39 | 40 | -keepclasseswithmembernames class * { 41 | native ; 42 | } 43 | 44 | -keep class android.support.v8.renderscript.** { *; } 45 | 46 | # Play Services 47 | 48 | -keep class * extends java.util.ListResourceBundle { 49 | protected Object[][] getContents(); 50 | } 51 | 52 | -keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable { 53 | public static final *** NULL; 54 | } 55 | 56 | -keepnames @com.google.android.gms.common.annotation.KeepName class * 57 | -keepclassmembernames class * { 58 | @com.google.android.gms.common.annotation.KeepName *; 59 | } 60 | 61 | -keepnames class * implements android.os.Parcelable { 62 | public static final ** CREATOR; 63 | } 64 | -------------------------------------------------------------------------------- /main/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 29 | 30 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 45 | 46 | 49 | 50 | 51 | 52 | 53 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /main/src/main/assets/Alegreya-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/assets/Alegreya-BlackItalic.ttf -------------------------------------------------------------------------------- /main/src/main/assets/Alegreya-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/assets/Alegreya-Italic.ttf -------------------------------------------------------------------------------- /main/src/main/assets/AlegreyaModifiedReadme: -------------------------------------------------------------------------------- 1 | Alegreya has been modified using fontbakery-fix-vertical-metrics.py [1] to have a descender of -100. 2 | 3 | [1] https://github.com/googlefonts/fontbakery/blob/master/tools/fontbakery-fix-vertical-metrics.py -------------------------------------------------------------------------------- /main/src/main/assets/kepler-01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/assets/kepler-01.jpg -------------------------------------------------------------------------------- /main/src/main/assets/kepler-02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/assets/kepler-02.jpg -------------------------------------------------------------------------------- /main/src/main/assets/kepler-03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/assets/kepler-03.jpg -------------------------------------------------------------------------------- /main/src/main/assets/starrynight.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/assets/starrynight.jpg -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/ArtDetailViewport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei; 19 | 20 | import android.graphics.RectF; 21 | 22 | import de.greenrobot.event.EventBus; 23 | 24 | // Singleton that also behaves as an event 25 | public class ArtDetailViewport { 26 | private volatile RectF mViewport0 = new RectF(); 27 | private volatile RectF mViewport1 = new RectF(); 28 | private boolean mFromUser; 29 | 30 | private static ArtDetailViewport sInstance = new ArtDetailViewport(); 31 | 32 | public static ArtDetailViewport getInstance() { 33 | return sInstance; 34 | } 35 | 36 | private ArtDetailViewport() { 37 | } 38 | 39 | public RectF getViewport(int id) { 40 | return (id == 0) ? mViewport0 : mViewport1; 41 | } 42 | 43 | public ArtDetailViewport setViewport(int id, RectF viewport, boolean fromUser) { 44 | return setViewport(id, viewport.left, viewport.top, viewport.right, viewport.bottom, 45 | fromUser); 46 | } 47 | 48 | public ArtDetailViewport setViewport(int id, float left, float top, float right, float bottom, 49 | boolean fromUser) { 50 | mFromUser = fromUser; 51 | getViewport(id).set(left, top, right, bottom); 52 | EventBus.getDefault().post(this); 53 | return this; 54 | } 55 | 56 | public boolean isFromUser() { 57 | return mFromUser; 58 | } 59 | 60 | public ArtDetailViewport setDefaultViewport(int id, float bitmapAspectRatio, 61 | float screenAspectRatio) { 62 | mFromUser = false; 63 | if (bitmapAspectRatio > screenAspectRatio) { 64 | getViewport(id).set( 65 | 0.5f - screenAspectRatio / bitmapAspectRatio / 2, 66 | 0, 67 | 0.5f + screenAspectRatio / bitmapAspectRatio / 2, 68 | 1); 69 | } else { 70 | getViewport(id).set( 71 | 0, 72 | 0.5f - bitmapAspectRatio / screenAspectRatio / 2, 73 | 1, 74 | 0.5f + bitmapAspectRatio / screenAspectRatio / 2); 75 | } 76 | EventBus.getDefault().post(this); 77 | return this; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/LockScreenVisibleReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei; 19 | 20 | import android.app.KeyguardManager; 21 | import android.content.BroadcastReceiver; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.content.IntentFilter; 25 | import android.content.SharedPreferences; 26 | import android.preference.PreferenceManager; 27 | 28 | import com.google.android.apps.muzei.event.LockScreenVisibleChangedEvent; 29 | 30 | import de.greenrobot.event.EventBus; 31 | 32 | public class LockScreenVisibleReceiver extends BroadcastReceiver { 33 | public static final String PREF_ENABLED = "disable_blur_when_screen_locked_enabled"; 34 | 35 | private boolean mRegistered = false; 36 | private Context mRegisterDeregisterContext; 37 | 38 | private SharedPreferences.OnSharedPreferenceChangeListener mOnSharedPreferenceChangeListener 39 | = new SharedPreferences.OnSharedPreferenceChangeListener() { 40 | @Override 41 | public void onSharedPreferenceChanged(SharedPreferences sp, String key) { 42 | if (PREF_ENABLED.equals(key)) { 43 | registerDeregister(sp.getBoolean(PREF_ENABLED, false)); 44 | } 45 | } 46 | }; 47 | 48 | @Override 49 | public void onReceive(Context context, Intent intent) { 50 | if (intent != null) { 51 | if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) { 52 | EventBus.getDefault().post(new LockScreenVisibleChangedEvent(false)); 53 | } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { 54 | EventBus.getDefault().post(new LockScreenVisibleChangedEvent(true)); 55 | } else if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) { 56 | KeyguardManager kgm = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); 57 | if (!kgm.inKeyguardRestrictedInputMode()) { 58 | EventBus.getDefault().post(new LockScreenVisibleChangedEvent(false)); 59 | } 60 | } 61 | } 62 | } 63 | 64 | private static IntentFilter createIntentFilter() { 65 | IntentFilter presentFilter = new IntentFilter(); 66 | presentFilter.addAction(Intent.ACTION_USER_PRESENT); 67 | presentFilter.addAction(Intent.ACTION_SCREEN_OFF); 68 | presentFilter.addAction(Intent.ACTION_SCREEN_ON); 69 | return presentFilter; 70 | } 71 | 72 | public void setupRegisterDeregister(Context context) { 73 | mRegisterDeregisterContext = context; 74 | PreferenceManager.getDefaultSharedPreferences(context) 75 | .registerOnSharedPreferenceChangeListener(mOnSharedPreferenceChangeListener); 76 | mOnSharedPreferenceChangeListener.onSharedPreferenceChanged( 77 | PreferenceManager.getDefaultSharedPreferences(context), PREF_ENABLED); 78 | } 79 | 80 | private void registerDeregister(boolean register) { 81 | if (mRegistered == register || mRegisterDeregisterContext == null) { 82 | return; 83 | } 84 | 85 | if (register) { 86 | mRegisterDeregisterContext.registerReceiver(this, createIntentFilter()); 87 | } else { 88 | mRegisterDeregisterContext.unregisterReceiver(this); 89 | } 90 | 91 | mRegistered = register; 92 | } 93 | 94 | public void destroy() { 95 | registerDeregister(false); 96 | if (mRegisterDeregisterContext != null) { 97 | PreferenceManager.getDefaultSharedPreferences(mRegisterDeregisterContext) 98 | .unregisterOnSharedPreferenceChangeListener(mOnSharedPreferenceChangeListener); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/event/ArtDetailOpenedClosedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.event; 19 | 20 | public class ArtDetailOpenedClosedEvent { 21 | boolean mOpened; 22 | 23 | public ArtDetailOpenedClosedEvent(boolean opened) { 24 | mOpened = opened; 25 | } 26 | 27 | public boolean isArtDetailOpened() { 28 | return mOpened; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/event/ArtworkSizeChangedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.event; 19 | 20 | public class ArtworkSizeChangedEvent { 21 | private int mWidth; 22 | private int mHeight; 23 | 24 | public ArtworkSizeChangedEvent(int width, int height) { 25 | mWidth = width; 26 | mHeight = height; 27 | } 28 | 29 | public int getWidth() { 30 | return mWidth; 31 | } 32 | 33 | public int getHeight() { 34 | return mHeight; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/event/LockScreenVisibleChangedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.event; 19 | 20 | public class LockScreenVisibleChangedEvent { 21 | public boolean mLockScreenVisible = false; 22 | 23 | public LockScreenVisibleChangedEvent(boolean lockScreenVisible) { 24 | mLockScreenVisible = lockScreenVisible; 25 | } 26 | 27 | public boolean isLockScreenVisible() { 28 | return mLockScreenVisible; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/event/SwitchingPhotosStateChangedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.event; 19 | 20 | public class SwitchingPhotosStateChangedEvent { 21 | private boolean mSwitchingPhotos; 22 | private int mId; 23 | 24 | public SwitchingPhotosStateChangedEvent(int id, boolean switchingPhotos) { 25 | mId = id; 26 | mSwitchingPhotos = switchingPhotos; 27 | } 28 | 29 | public int getCurrentId() { 30 | return mId; 31 | } 32 | 33 | public boolean isSwitchingPhotos() { 34 | return mSwitchingPhotos; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/event/WallpaperActiveStateChangedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.event; 19 | 20 | public class WallpaperActiveStateChangedEvent { 21 | private boolean mActive; 22 | 23 | public WallpaperActiveStateChangedEvent(boolean active) { 24 | mActive = active; 25 | } 26 | 27 | public boolean isActive() { 28 | return mActive; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/event/WallpaperSizeChangedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.event; 19 | 20 | public class WallpaperSizeChangedEvent { 21 | private int mWidth; 22 | private int mHeight; 23 | 24 | public WallpaperSizeChangedEvent(int width, int height) { 25 | mWidth = width; 26 | mHeight = height; 27 | } 28 | 29 | public int getWidth() { 30 | return mWidth; 31 | } 32 | 33 | public int getHeight() { 34 | return mHeight; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/render/BitmapRegionLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.render; 19 | 20 | import android.graphics.Bitmap; 21 | import android.graphics.BitmapRegionDecoder; 22 | import android.graphics.Matrix; 23 | import android.graphics.Rect; 24 | 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | 28 | import static android.graphics.BitmapFactory.Options; 29 | 30 | /** 31 | * Wrapper for {@link BitmapRegionDecoder} with some extra functionality. 32 | */ 33 | public class BitmapRegionLoader { 34 | private boolean mValid = false; 35 | private int mRotation = 0; 36 | private int mOriginalWidth; 37 | private int mOriginalHeight; 38 | private Rect mTempRect = new Rect(); 39 | private InputStream mInputStream; 40 | private volatile BitmapRegionDecoder mBitmapRegionDecoder; 41 | private Matrix mRotateMatrix; 42 | 43 | public static BitmapRegionLoader newInstance(InputStream in) throws IOException { 44 | return newInstance(in, 0); 45 | } 46 | 47 | public static BitmapRegionLoader newInstance(InputStream in, int rotation) throws IOException { 48 | if (in == null) { 49 | return null; 50 | } 51 | 52 | BitmapRegionLoader loader = new BitmapRegionLoader(in); 53 | if (loader.mValid) { 54 | loader.mRotation = rotation; 55 | if (loader.mRotation != 0) { 56 | loader.mRotateMatrix = new Matrix(); 57 | loader.mRotateMatrix.postRotate(rotation); 58 | } 59 | return loader; 60 | } 61 | 62 | return null; 63 | } 64 | 65 | private BitmapRegionLoader(InputStream in) throws IOException { 66 | mInputStream = in; 67 | mBitmapRegionDecoder = BitmapRegionDecoder.newInstance(in, false); 68 | if (mBitmapRegionDecoder != null) { 69 | mOriginalWidth = mBitmapRegionDecoder.getWidth(); 70 | mOriginalHeight = mBitmapRegionDecoder.getHeight(); 71 | mValid = true; 72 | } 73 | } 74 | 75 | /** 76 | * Key difference, aside from support for rotation, from 77 | * {@link BitmapRegionDecoder#decodeRegion(Rect, Options)} in this implementation is that even 78 | * if inBitmap is given, a sub-bitmap might be returned. 79 | */ 80 | public synchronized Bitmap decodeRegion(Rect rect, Options options) { 81 | int unsampledInBitmapWidth = -1; 82 | int unsampledInBitmapHeight = -1; 83 | int sampleSize = Math.max(1, options != null ? options.inSampleSize : 1); 84 | if (options != null && options.inBitmap != null) { 85 | unsampledInBitmapWidth = options.inBitmap.getWidth() * sampleSize; 86 | unsampledInBitmapHeight = options.inBitmap.getHeight() * sampleSize; 87 | } 88 | 89 | // Decode with rotation 90 | switch (mRotation) { 91 | case 90: 92 | mTempRect.set( 93 | rect.top, mOriginalHeight - rect.right, 94 | rect.bottom, mOriginalHeight - rect.left); 95 | break; 96 | 97 | case 180: 98 | mTempRect.set( 99 | mOriginalWidth - rect.right, mOriginalHeight - rect.bottom, 100 | mOriginalWidth - rect.left, mOriginalHeight - rect.top); 101 | break; 102 | 103 | case 270: 104 | mTempRect.set( 105 | mOriginalWidth - rect.bottom, rect.left, 106 | mOriginalWidth - rect.top, rect.right); 107 | break; 108 | 109 | default: 110 | mTempRect.set(rect); 111 | } 112 | 113 | Bitmap bitmap = mBitmapRegionDecoder.decodeRegion(mTempRect, options); 114 | if (bitmap == null) { 115 | return null; 116 | } 117 | 118 | if (options != null && options.inBitmap != null && 119 | ((mTempRect.width() != unsampledInBitmapWidth 120 | || mTempRect.height() != unsampledInBitmapHeight))) { 121 | // Need to extract the sub-bitmap 122 | Bitmap subBitmap = Bitmap.createBitmap( 123 | bitmap, 0, 0, 124 | mTempRect.width() / sampleSize, 125 | mTempRect.height() / sampleSize); 126 | if (bitmap != options.inBitmap && bitmap != subBitmap) { 127 | bitmap.recycle(); 128 | } 129 | bitmap = subBitmap; 130 | } 131 | 132 | if (mRotateMatrix != null) { 133 | // Rotate decoded bitmap 134 | Bitmap rotatedBitmap = Bitmap.createBitmap( 135 | bitmap, 0, 0, 136 | bitmap.getWidth(), bitmap.getHeight(), 137 | mRotateMatrix, true); 138 | if ((options == null || bitmap != options.inBitmap) && bitmap != rotatedBitmap) { 139 | bitmap.recycle(); 140 | } 141 | bitmap = rotatedBitmap; 142 | } 143 | 144 | return bitmap; 145 | } 146 | 147 | public synchronized int getWidth() { 148 | return (mRotation == 90 || mRotation == 270) ? mOriginalHeight : mOriginalWidth; 149 | } 150 | 151 | public synchronized int getHeight() { 152 | return (mRotation == 90 || mRotation == 270) ? mOriginalWidth : mOriginalHeight; 153 | } 154 | 155 | public synchronized void destroy() { 156 | mBitmapRegionDecoder.recycle(); 157 | mBitmapRegionDecoder = null; 158 | try { 159 | mInputStream.close(); 160 | } catch (IOException ignored) { 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/render/GLColorOverlay.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.render; 19 | 20 | import android.graphics.Color; 21 | import android.opengl.GLES20; 22 | 23 | import java.nio.FloatBuffer; 24 | 25 | class GLColorOverlay { 26 | private static final String VERTEX_SHADER_CODE = "" + 27 | // This matrix member variable provides a hook to manipulate 28 | // the coordinates of the objects that use this vertex shader 29 | "uniform mat4 uMVPMatrix;" + 30 | "attribute vec4 aPosition;" + 31 | "void main(){" + 32 | " gl_Position = uMVPMatrix * aPosition;" + 33 | "}"; 34 | 35 | private static final String FRAGMENT_SHADER_CODE = "" + 36 | "precision mediump float;" + 37 | "uniform sampler2D uTexture;" + 38 | "uniform vec4 uColor;" + 39 | "void main(){" + 40 | " gl_FragColor = uColor;" + 41 | "}"; 42 | 43 | // number of coordinates per vertex in this array 44 | private static final int COORDS_PER_VERTEX = 3; 45 | private static final int VERTEX_STRIDE_BYTES = COORDS_PER_VERTEX * GLUtil.BYTES_PER_FLOAT; 46 | 47 | private float mVertices[] = { 48 | -1, 1, 0, // top left 49 | -1, -1, 0, // bottom left 50 | 1, -1, 0, // bottom right 51 | 52 | -1, 1, 0, // top left 53 | 1, -1, 0, // bottom right 54 | 1, 1, 0, // top right 55 | }; 56 | 57 | private int mColor; 58 | 59 | private FloatBuffer mVertexBuffer; 60 | 61 | private static int sProgramHandle; 62 | private static int sAttribPositionHandle; 63 | private static int sUniformColorHandle; 64 | private static int sUniformMVPMatrixHandle; 65 | 66 | public GLColorOverlay(int color) { 67 | mColor = color; 68 | 69 | mVertexBuffer = GLUtil.asFloatBuffer(mVertices); 70 | } 71 | 72 | public static void initGl() { 73 | // Initialize shaders and create/link program 74 | int vertexShaderHandle = GLUtil.loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_CODE); 75 | int fragShaderHandle = GLUtil.loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE); 76 | 77 | sProgramHandle = GLUtil.createAndLinkProgram(vertexShaderHandle, fragShaderHandle, null); 78 | sAttribPositionHandle = GLES20.glGetAttribLocation(sProgramHandle, "aPosition"); 79 | sUniformMVPMatrixHandle = GLES20.glGetUniformLocation(sProgramHandle, "uMVPMatrix"); 80 | sUniformColorHandle = GLES20.glGetUniformLocation(sProgramHandle, "uColor"); 81 | } 82 | 83 | public void draw(float[] mvpMatrix) { 84 | // Add program to OpenGL ES environment 85 | GLES20.glUseProgram(sProgramHandle); 86 | 87 | // Pass in the vertex information 88 | GLES20.glEnableVertexAttribArray(sAttribPositionHandle); 89 | GLES20.glVertexAttribPointer(sAttribPositionHandle, 90 | COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 91 | VERTEX_STRIDE_BYTES, mVertexBuffer); 92 | 93 | // Apply the projection and view transformation 94 | GLES20.glUniformMatrix4fv(sUniformMVPMatrixHandle, 1, false, mvpMatrix, 0); 95 | GLUtil.checkGlError("glUniformMatrix4fv"); 96 | 97 | // Set the alpha 98 | float r = Color.red(mColor) * 1f / 255; 99 | float g = Color.green(mColor) * 1f / 255; 100 | float b = Color.blue(mColor) * 1f / 255; 101 | float a = Color.alpha(mColor) * 1f / 255; 102 | GLES20.glUniform4f(sUniformColorHandle, r, g, b, a); 103 | 104 | // Draw the triangle 105 | GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mVertices.length / COORDS_PER_VERTEX); 106 | 107 | GLES20.glDisableVertexAttribArray(sAttribPositionHandle); 108 | } 109 | 110 | public void setColor(int color) { 111 | mColor = color; 112 | } 113 | 114 | public void destroy() { 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/render/GLUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.render; 19 | 20 | import android.graphics.Bitmap; 21 | import android.opengl.GLES20; 22 | import android.opengl.GLUtils; 23 | 24 | import com.google.android.apps.muzei.util.LogUtil; 25 | 26 | import net.nurik.roman.muzei.BuildConfig; 27 | 28 | import java.nio.ByteBuffer; 29 | import java.nio.ByteOrder; 30 | import java.nio.FloatBuffer; 31 | 32 | import static com.google.android.apps.muzei.util.LogUtil.LOGE; 33 | 34 | public class GLUtil { 35 | private static final String TAG = LogUtil.makeLogTag(GLUtil.class); 36 | 37 | public static final int BYTES_PER_FLOAT = 4; 38 | 39 | public static int loadShader(int type, String shaderCode) { 40 | // create a vertex shader type (GLES20.GL_VERTEX_SHADER) 41 | // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) 42 | int shaderHandle = GLES20.glCreateShader(type); 43 | 44 | // add the source code to the shader and compile it 45 | GLES20.glShaderSource(shaderHandle, shaderCode); 46 | GLES20.glCompileShader(shaderHandle); 47 | checkGlError("glCompileShader"); 48 | return shaderHandle; 49 | } 50 | 51 | public static int createAndLinkProgram(int vertexShaderHandle, int fragShaderHandle, 52 | String[] attributes) { 53 | int programHandle = GLES20.glCreateProgram(); 54 | GLUtil.checkGlError("glCreateProgram"); 55 | GLES20.glAttachShader(programHandle, vertexShaderHandle); 56 | GLES20.glAttachShader(programHandle, fragShaderHandle); 57 | if (attributes != null) { 58 | final int size = attributes.length; 59 | for (int i = 0; i < size; i++) { 60 | GLES20.glBindAttribLocation(programHandle, i, attributes[i]); 61 | } 62 | } 63 | GLES20.glLinkProgram(programHandle); 64 | GLUtil.checkGlError("glLinkProgram"); 65 | GLES20.glDeleteShader(vertexShaderHandle); 66 | GLES20.glDeleteShader(fragShaderHandle); 67 | return programHandle; 68 | } 69 | 70 | public static int loadTexture(Bitmap bitmap) { 71 | final int[] textureHandle = new int[1]; 72 | 73 | GLES20.glGenTextures(1, textureHandle, 0); 74 | GLUtil.checkGlError("glGenTextures"); 75 | 76 | if (textureHandle[0] != 0) { 77 | // Bind to the texture in OpenGL 78 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]); 79 | 80 | // Set filtering 81 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, 82 | GLES20.GL_CLAMP_TO_EDGE); 83 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, 84 | GLES20.GL_CLAMP_TO_EDGE); 85 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, 86 | GLES20.GL_LINEAR); 87 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, 88 | GLES20.GL_LINEAR); 89 | 90 | // Load the bitmap into the bound texture. 91 | GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); 92 | GLUtil.checkGlError("texImage2D"); 93 | } 94 | 95 | if (textureHandle[0] == 0) { 96 | LOGE(TAG, "Error loading texture (empty texture handle)"); 97 | if (BuildConfig.DEBUG) { 98 | throw new RuntimeException("Error loading texture (empty texture handle)."); 99 | } 100 | } 101 | 102 | return textureHandle[0]; 103 | } 104 | 105 | public static void checkGlError(String glOperation) { 106 | int error; 107 | while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { 108 | LOGE(TAG, glOperation + ": glError " + error); 109 | if (BuildConfig.DEBUG) { 110 | throw new RuntimeException(glOperation + ": glError " + error); 111 | } 112 | } 113 | } 114 | 115 | public static FloatBuffer asFloatBuffer(float[] array) { 116 | FloatBuffer buffer = newFloatBuffer(array.length); 117 | buffer.put(array); 118 | buffer.position(0); 119 | return buffer; 120 | } 121 | 122 | public static FloatBuffer newFloatBuffer(int size) { 123 | FloatBuffer buffer = ByteBuffer.allocateDirect(size * BYTES_PER_FLOAT) 124 | .order(ByteOrder.nativeOrder()) 125 | .asFloatBuffer(); 126 | buffer.position(0); 127 | return buffer; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/render/ImageUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.render; 19 | 20 | import android.graphics.Bitmap; 21 | import android.graphics.Color; 22 | 23 | public class ImageUtil { 24 | // Make sure input images are very small! 25 | public static float calculateDarkness(Bitmap bitmap) { 26 | if (bitmap == null) { 27 | return 0; 28 | } 29 | 30 | int width = bitmap.getWidth(); 31 | int height = bitmap.getHeight(); 32 | 33 | int totalLum = 0; 34 | int n = 0; 35 | int x, y, color; 36 | for (y = 0; y < height; y++) { 37 | for (x = 0; x < width; x++) { 38 | ++n; 39 | color = bitmap.getPixel(x, y); 40 | totalLum += (0.21f * Color.red(color) 41 | + 0.71f * Color.green(color) 42 | + 0.07f * Color.blue(color)); 43 | } 44 | } 45 | 46 | return (totalLum / n) / 256f; 47 | } 48 | 49 | private ImageUtil() { 50 | } 51 | 52 | public static int calculateSampleSize(int rawSize, int targetSize) { 53 | int sampleSize = 1; 54 | while (rawSize / (sampleSize << 1) > targetSize) { 55 | sampleSize <<= 1; 56 | } 57 | return sampleSize; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/render/LocalRenderController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.render; 19 | 20 | import android.content.Context; 21 | import android.content.SharedPreferences; 22 | import android.os.Handler; 23 | import android.preference.PreferenceManager; 24 | 25 | import com.google.android.apps.muzei.util.LogUtil; 26 | 27 | import java.io.IOException; 28 | 29 | import static com.google.android.apps.muzei.util.LogUtil.LOGE; 30 | 31 | public class LocalRenderController extends RenderController { 32 | private static final String TAG = LogUtil.makeLogTag(LocalRenderController.class); 33 | 34 | private static final String PREF_LOCAL_IMAGE_INDEX = "local_image_index"; 35 | 36 | static final String LOCAL_IMAGES[] = { 37 | "kepler-01.jpg", 38 | "kepler-02.jpg", 39 | "kepler-03.jpg", 40 | }; 41 | 42 | private final Handler mHandler = new Handler(); 43 | 44 | public LocalRenderController(Context context, MuzeiBlurRenderer renderer, 45 | Callbacks callbacks) { 46 | super(context, renderer, callbacks); 47 | } 48 | 49 | @Override 50 | public void destroy() { 51 | super.destroy(); 52 | mHandler.removeCallbacksAndMessages(null); 53 | } 54 | 55 | @Override 56 | protected BitmapRegionLoader openDownloadedCurrentArtwork(boolean forceReload) { 57 | try { 58 | final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); 59 | int index = sp.getInt(PREF_LOCAL_IMAGE_INDEX, 0); 60 | int nextIndex = index+1; 61 | if (nextIndex >= LOCAL_IMAGES.length) { 62 | nextIndex = 0; 63 | } 64 | sp.edit().putInt(PREF_LOCAL_IMAGE_INDEX, nextIndex).apply(); 65 | 66 | return BitmapRegionLoader.newInstance(mContext.getAssets().open(LOCAL_IMAGES[index])); 67 | } catch (IOException e) { 68 | LOGE(TAG, "Error opening demo image.", e); 69 | return null; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/render/RenderController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.render; 19 | 20 | import android.content.Context; 21 | import android.os.AsyncTask; 22 | import android.os.Handler; 23 | import android.os.Message; 24 | 25 | 26 | public abstract class RenderController { 27 | protected Context mContext; 28 | protected MuzeiBlurRenderer mRenderer; 29 | protected Callbacks mCallbacks; 30 | protected boolean mVisible; 31 | private BitmapRegionLoader mQueuedBitmapRegionLoader; 32 | 33 | public RenderController(Context context, MuzeiBlurRenderer renderer, Callbacks callbacks) { 34 | mRenderer = renderer; 35 | mContext = context; 36 | mCallbacks = callbacks; 37 | } 38 | 39 | public void destroy() { 40 | if (mQueuedBitmapRegionLoader != null) { 41 | mQueuedBitmapRegionLoader.destroy(); 42 | } 43 | } 44 | 45 | private void throttledForceReloadCurrentArtwork() { 46 | mThrottledForceReloadHandler.removeMessages(0); 47 | mThrottledForceReloadHandler.sendEmptyMessageDelayed(0, 250); 48 | } 49 | 50 | private Handler mThrottledForceReloadHandler = new Handler() { 51 | @Override 52 | public void handleMessage(Message msg) { 53 | reloadCurrentArtwork(true); 54 | } 55 | }; 56 | 57 | protected abstract BitmapRegionLoader openDownloadedCurrentArtwork(boolean forceReload); 58 | 59 | public void reloadCurrentArtwork(final boolean forceReload) { 60 | new AsyncTask() { 61 | @Override 62 | protected BitmapRegionLoader doInBackground(Void... voids) { 63 | // openDownloadedCurrentArtwork should be called on a background thread 64 | return openDownloadedCurrentArtwork(forceReload); 65 | } 66 | 67 | @Override 68 | protected void onPostExecute(final BitmapRegionLoader bitmapRegionLoader) { 69 | if (bitmapRegionLoader == null) { 70 | return; 71 | } 72 | 73 | mCallbacks.queueEventOnGlThread(new Runnable() { 74 | @Override 75 | public void run() { 76 | if (mVisible) { 77 | mRenderer.setAndConsumeBitmapRegionLoader(bitmapRegionLoader); 78 | } else { 79 | mQueuedBitmapRegionLoader = bitmapRegionLoader; 80 | } 81 | } 82 | }); 83 | } 84 | }.execute((Void) null); 85 | } 86 | 87 | public void setVisible(boolean visible) { 88 | mVisible = visible; 89 | if (visible) { 90 | mCallbacks.queueEventOnGlThread(new Runnable() { 91 | @Override 92 | public void run() { 93 | if (mQueuedBitmapRegionLoader != null) { 94 | mRenderer.setAndConsumeBitmapRegionLoader(mQueuedBitmapRegionLoader); 95 | mQueuedBitmapRegionLoader = null; 96 | } 97 | } 98 | }); 99 | mCallbacks.requestRender(); 100 | } 101 | } 102 | 103 | public static interface Callbacks { 104 | void queueEventOnGlThread(Runnable runnable); 105 | void requestRender(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/settings/Prefs.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.settings; 19 | 20 | /** 21 | * Preference constants/helpers. 22 | */ 23 | public interface Prefs { 24 | public static final String PREF_GREY_AMOUNT = "grey_amount"; 25 | public static final String PREF_DIM_AMOUNT = "dim_amount"; 26 | public static final String PREF_BLUR_AMOUNT = "blur_amount"; 27 | } 28 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/util/CheatSheet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.util; 19 | 20 | import android.content.Context; 21 | import android.graphics.Rect; 22 | import android.text.TextUtils; 23 | import android.view.Gravity; 24 | import android.view.View; 25 | import android.widget.Toast; 26 | 27 | /** 28 | * Helper class for showing cheat sheets (tooltips) for icon-only UI elements on long-press. This is 29 | * already default platform behavior for icon-only {@link android.app.ActionBar} items and tabs. 30 | * This class provides this behavior for any other such UI element. 31 | * 32 | *

Based on the original action bar implementation in 33 | * ActionMenuItemView.java. 34 | */ 35 | public class CheatSheet { 36 | /** 37 | * The estimated height of a toast, in dips (density-independent pixels). This is used to 38 | * determine whether or not the toast should appear above or below the UI element. 39 | */ 40 | private static final int ESTIMATED_TOAST_HEIGHT_DIPS = 48; 41 | 42 | /** 43 | * Sets up a cheat sheet (tooltip) for the given view by setting its {@link 44 | * android.view.View.OnLongClickListener}. When the view is long-pressed, a {@link Toast} with 45 | * the view's {@link android.view.View#getContentDescription() content description} will be 46 | * shown either above (default) or below the view (if there isn't room above it). 47 | * 48 | * @param view The view to add a cheat sheet for. 49 | */ 50 | public static void setup(View view) { 51 | view.setOnLongClickListener(new View.OnLongClickListener() { 52 | @Override 53 | public boolean onLongClick(View view) { 54 | return showCheatSheet(view, view.getContentDescription()); 55 | } 56 | }); 57 | } 58 | 59 | /** 60 | * Sets up a cheat sheet (tooltip) for the given view by setting its {@link 61 | * android.view.View.OnLongClickListener}. When the view is long-pressed, a {@link Toast} with 62 | * the given text will be shown either above (default) or below the view (if there isn't room 63 | * above it). 64 | * 65 | * @param view The view to add a cheat sheet for. 66 | * @param textResId The string resource containing the text to show on long-press. 67 | */ 68 | public static void setup(View view, final int textResId) { 69 | view.setOnLongClickListener(new View.OnLongClickListener() { 70 | @Override 71 | public boolean onLongClick(View view) { 72 | return showCheatSheet(view, view.getContext().getString(textResId)); 73 | } 74 | }); 75 | } 76 | 77 | /** 78 | * Sets up a cheat sheet (tooltip) for the given view by setting its {@link 79 | * android.view.View.OnLongClickListener}. When the view is long-pressed, a {@link Toast} with 80 | * the given text will be shown either above (default) or below the view (if there isn't room 81 | * above it). 82 | * 83 | * @param view The view to add a cheat sheet for. 84 | * @param text The text to show on long-press. 85 | */ 86 | public static void setup(View view, final CharSequence text) { 87 | view.setOnLongClickListener(new View.OnLongClickListener() { 88 | @Override 89 | public boolean onLongClick(View view) { 90 | return showCheatSheet(view, text); 91 | } 92 | }); 93 | } 94 | 95 | /** 96 | * Removes the cheat sheet for the given view by removing the view's {@link 97 | * android.view.View.OnLongClickListener}. 98 | * 99 | * @param view The view whose cheat sheet should be removed. 100 | */ 101 | public static void remove(final View view) { 102 | view.setOnLongClickListener(null); 103 | } 104 | 105 | /** 106 | * Internal helper method to show the cheat sheet toast. 107 | */ 108 | private static boolean showCheatSheet(View view, CharSequence text) { 109 | if (TextUtils.isEmpty(text)) { 110 | return false; 111 | } 112 | 113 | final int[] screenPos = new int[2]; // origin is device display 114 | final Rect displayFrame = new Rect(); // includes decorations (e.g. status bar) 115 | view.getLocationOnScreen(screenPos); 116 | view.getWindowVisibleDisplayFrame(displayFrame); 117 | 118 | final Context context = view.getContext(); 119 | final int viewWidth = view.getWidth(); 120 | final int viewHeight = view.getHeight(); 121 | final int viewCenterX = screenPos[0] + viewWidth / 2; 122 | final int screenWidth = context.getResources().getDisplayMetrics().widthPixels; 123 | final int estimatedToastHeight = (int) (ESTIMATED_TOAST_HEIGHT_DIPS 124 | * context.getResources().getDisplayMetrics().density); 125 | 126 | Toast cheatSheet = Toast.makeText(context, text, Toast.LENGTH_SHORT); 127 | boolean showBelow = screenPos[1] < estimatedToastHeight; 128 | if (showBelow) { 129 | // Show below 130 | // Offsets are after decorations (e.g. status bar) are factored in 131 | cheatSheet.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 132 | viewCenterX - screenWidth / 2, 133 | screenPos[1] - displayFrame.top + viewHeight); 134 | } else { 135 | // Show above 136 | // Offsets are after decorations (e.g. status bar) are factored in 137 | // NOTE: We can't use Gravity.BOTTOM because when the keyboard is up 138 | // its height isn't factored in. 139 | cheatSheet.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 140 | viewCenterX - screenWidth / 2, 141 | screenPos[1] - displayFrame.top - estimatedToastHeight); 142 | } 143 | 144 | cheatSheet.show(); 145 | return true; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/util/DrawInsetsFrameLayout.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.util; 19 | 20 | import android.content.Context; 21 | import android.content.res.TypedArray; 22 | import android.graphics.Canvas; 23 | import android.graphics.Rect; 24 | import android.graphics.drawable.Drawable; 25 | import android.util.AttributeSet; 26 | import android.widget.FrameLayout; 27 | 28 | import net.nurik.roman.muzei.R; 29 | 30 | public class DrawInsetsFrameLayout extends FrameLayout { 31 | private Drawable mInsetBackground; 32 | private Drawable mTopInsetBackground; 33 | private Drawable mBottomInsetBackground; 34 | private Drawable mSideInsetBackground; 35 | 36 | private Rect mInsets; 37 | private Rect mTempRect = new Rect(); 38 | private OnInsetsCallback mOnInsetsCallback; 39 | 40 | public DrawInsetsFrameLayout(Context context) { 41 | super(context); 42 | init(context, null, 0); 43 | } 44 | 45 | public DrawInsetsFrameLayout(Context context, AttributeSet attrs) { 46 | super(context, attrs); 47 | init(context, attrs, 0); 48 | } 49 | 50 | public DrawInsetsFrameLayout(Context context, AttributeSet attrs, int defStyle) { 51 | super(context, attrs, defStyle); 52 | init(context, attrs, defStyle); 53 | } 54 | 55 | private void init(Context context, AttributeSet attrs, int defStyle) { 56 | final TypedArray a = context.obtainStyledAttributes(attrs, 57 | R.styleable.DrawInsetsFrameLayout, defStyle, 0); 58 | assert a != null; 59 | 60 | mInsetBackground = a.getDrawable(R.styleable.DrawInsetsFrameLayout_insetBackground); 61 | mTopInsetBackground = a.getDrawable(R.styleable.DrawInsetsFrameLayout_topInsetBackground); 62 | mBottomInsetBackground = 63 | a.getDrawable(R.styleable.DrawInsetsFrameLayout_bottomInsetBackground); 64 | mSideInsetBackground = a.getDrawable(R.styleable.DrawInsetsFrameLayout_sideInsetBackground); 65 | 66 | a.recycle(); 67 | } 68 | 69 | @Override 70 | protected void onAttachedToWindow() { 71 | super.onAttachedToWindow(); 72 | if (mInsetBackground != null) { 73 | mInsetBackground.setCallback(this); 74 | } 75 | if (mTopInsetBackground != null) { 76 | mTopInsetBackground.setCallback(this); 77 | } 78 | if (mBottomInsetBackground != null) { 79 | mBottomInsetBackground.setCallback(this); 80 | } 81 | if (mSideInsetBackground != null) { 82 | mSideInsetBackground.setCallback(this); 83 | } 84 | } 85 | 86 | @Override 87 | protected void onDetachedFromWindow() { 88 | super.onDetachedFromWindow(); 89 | if (mInsetBackground != null) { 90 | mInsetBackground.setCallback(null); 91 | } 92 | if (mTopInsetBackground != null) { 93 | mTopInsetBackground.setCallback(null); 94 | } 95 | if (mBottomInsetBackground != null) { 96 | mBottomInsetBackground.setCallback(null); 97 | } 98 | if (mSideInsetBackground != null) { 99 | mSideInsetBackground.setCallback(null); 100 | } 101 | } 102 | 103 | public void setOnInsetsCallback(OnInsetsCallback onInsetsCallback) { 104 | mOnInsetsCallback = onInsetsCallback; 105 | } 106 | 107 | @Override 108 | protected boolean fitSystemWindows(Rect insets) { 109 | mInsets = new Rect(insets); 110 | setWillNotDraw(false); 111 | postInvalidateOnAnimation(); 112 | if (mOnInsetsCallback != null) { 113 | mOnInsetsCallback.onInsetsChanged(insets); 114 | } 115 | return true; 116 | } 117 | 118 | @Override 119 | protected void onDraw(Canvas canvas) { 120 | super.onDraw(canvas); 121 | int width = getWidth(); 122 | int height = getHeight(); 123 | 124 | if (mInsets != null) { 125 | // Top 126 | mTempRect.set(0, 0, width, mInsets.top); 127 | if (mInsetBackground != null) { 128 | mInsetBackground.setBounds(mTempRect); 129 | mInsetBackground.draw(canvas); 130 | } 131 | if (mTopInsetBackground != null) { 132 | mTopInsetBackground.setBounds(mTempRect); 133 | mTopInsetBackground.draw(canvas); 134 | } 135 | 136 | // Bottom 137 | mTempRect.set(0, height - mInsets.bottom, width, height); 138 | if (mInsetBackground != null) { 139 | mInsetBackground.setBounds(mTempRect); 140 | mInsetBackground.draw(canvas); 141 | } 142 | if (mTopInsetBackground != null) { 143 | mBottomInsetBackground.setBounds(mTempRect); 144 | mBottomInsetBackground.draw(canvas); 145 | } 146 | 147 | // Left 148 | mTempRect.set(0, mInsets.top, mInsets.left, height - mInsets.bottom); 149 | if (mInsetBackground != null) { 150 | mInsetBackground.setBounds(mTempRect); 151 | mInsetBackground.draw(canvas); 152 | } 153 | if (mSideInsetBackground != null) { 154 | mSideInsetBackground.setBounds(mTempRect); 155 | mSideInsetBackground.draw(canvas); 156 | } 157 | 158 | // Right 159 | mTempRect.set(width - mInsets.right, mInsets.top, width, height - mInsets.bottom); 160 | if (mInsetBackground != null) { 161 | mInsetBackground.setBounds(mTempRect); 162 | mInsetBackground.draw(canvas); 163 | } 164 | if (mSideInsetBackground != null) { 165 | mSideInsetBackground.setBounds(mTempRect); 166 | mSideInsetBackground.draw(canvas); 167 | } 168 | } 169 | } 170 | 171 | public static interface OnInsetsCallback { 172 | public void onInsetsChanged(Rect insets); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/util/ImageBlurrer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.util; 19 | 20 | import android.content.Context; 21 | import android.graphics.Bitmap; 22 | import android.support.v8.renderscript.Allocation; 23 | import android.support.v8.renderscript.Element; 24 | import android.support.v8.renderscript.Matrix3f; 25 | import android.support.v8.renderscript.RSInvalidStateException; 26 | import android.support.v8.renderscript.RenderScript; 27 | import android.support.v8.renderscript.ScriptIntrinsicBlur; 28 | import android.support.v8.renderscript.ScriptIntrinsicColorMatrix; 29 | 30 | public class ImageBlurrer { 31 | public static final int MAX_SUPPORTED_BLUR_PIXELS = 25; 32 | private RenderScript mRS; 33 | 34 | private ScriptIntrinsicBlur mSIBlur; 35 | private ScriptIntrinsicColorMatrix mSIGrey; 36 | private Allocation mTmp1; 37 | private Allocation mTmp2; 38 | 39 | public ImageBlurrer(Context context) { 40 | mRS = RenderScript.create(context); 41 | mSIBlur = ScriptIntrinsicBlur.create(mRS, Element.U8_4(mRS)); 42 | mSIGrey = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS)); 43 | } 44 | 45 | public Bitmap blurBitmap(Bitmap src, float radius, float desaturateAmount) { 46 | if (src == null) { 47 | return null; 48 | } 49 | 50 | Bitmap dest = Bitmap.createBitmap(src); 51 | if (radius == 0f && desaturateAmount == 0f) { 52 | return dest; 53 | } 54 | 55 | if (mTmp1 != null) { 56 | mTmp1.destroy(); 57 | } 58 | if (mTmp2 != null) { 59 | try { 60 | mTmp2.destroy(); 61 | } catch (RSInvalidStateException e) { 62 | // Ignore 'Object already destroyed' exceptions 63 | } 64 | } 65 | 66 | mTmp1 = Allocation.createFromBitmap(mRS, src); 67 | mTmp2 = Allocation.createFromBitmap(mRS, dest); 68 | 69 | if (radius > 0f && desaturateAmount > 0f) { 70 | doBlur(radius, mTmp1, mTmp2); 71 | doDesaturate(MathUtil.constrain(0, 1, desaturateAmount), mTmp2, mTmp1); 72 | mTmp1.copyTo(dest); 73 | } else if (radius > 0f) { 74 | doBlur(radius, mTmp1, mTmp2); 75 | mTmp2.copyTo(dest); 76 | } else { 77 | doDesaturate(MathUtil.constrain(0, 1, desaturateAmount), mTmp1, mTmp2); 78 | mTmp2.copyTo(dest); 79 | } 80 | return dest; 81 | } 82 | 83 | private void doBlur(float amount, Allocation input, Allocation output) { 84 | mSIBlur.setRadius(amount); 85 | mSIBlur.setInput(input); 86 | mSIBlur.forEach(output); 87 | } 88 | 89 | private void doDesaturate(float normalizedAmount, Allocation input, Allocation output) { 90 | Matrix3f m = new Matrix3f(new float[]{ 91 | MathUtil.interpolate(1, 0.299f, normalizedAmount), 92 | MathUtil.interpolate(0, 0.299f, normalizedAmount), 93 | MathUtil.interpolate(0, 0.299f, normalizedAmount), 94 | 95 | MathUtil.interpolate(0, 0.587f, normalizedAmount), 96 | MathUtil.interpolate(1, 0.587f, normalizedAmount), 97 | MathUtil.interpolate(0, 0.587f, normalizedAmount), 98 | 99 | MathUtil.interpolate(0, 0.114f, normalizedAmount), 100 | MathUtil.interpolate(0, 0.114f, normalizedAmount), 101 | MathUtil.interpolate(1, 0.114f, normalizedAmount), 102 | }); 103 | mSIGrey.setColorMatrix(m); 104 | mSIGrey.forEach(input, output); 105 | } 106 | 107 | public void destroy() { 108 | mSIBlur.destroy(); 109 | if (mTmp1 != null) { 110 | mTmp1.destroy(); 111 | } 112 | if (mTmp2 != null) { 113 | mTmp2.destroy(); 114 | } 115 | mRS.destroy(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/util/LogUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.util; 19 | 20 | import android.util.Log; 21 | 22 | import net.nurik.roman.muzei.BuildConfig; 23 | 24 | /** 25 | * Helper methods that make logging more consistent throughout the app. 26 | */ 27 | public class LogUtil { 28 | private static final String TAG = makeLogTag(LogUtil.class); 29 | 30 | private static final String LOG_PREFIX = "muzei_"; 31 | private static final int LOG_PREFIX_LENGTH = LOG_PREFIX.length(); 32 | private static final int MAX_LOG_TAG_LENGTH = 23; 33 | 34 | private LogUtil() { 35 | } 36 | 37 | public static String makeLogTag(String str) { 38 | if (str.length() > MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH) { 39 | return LOG_PREFIX + str.substring(0, MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH - 1); 40 | } 41 | 42 | return LOG_PREFIX + str; 43 | } 44 | 45 | /** 46 | * WARNING: Don't use this when obfuscating class names with Proguard! 47 | */ 48 | public static String makeLogTag(Class cls) { 49 | return makeLogTag(cls.getSimpleName()); 50 | } 51 | 52 | public static void LOGD(final String tag, String message) { 53 | //noinspection PointlessBooleanExpression,ConstantConditions 54 | if (BuildConfig.DEBUG || Log.isLoggable(tag, Log.DEBUG)) { 55 | Log.d(tag, message); 56 | } 57 | } 58 | 59 | public static void LOGD(final String tag, String message, Throwable cause) { 60 | //noinspection PointlessBooleanExpression,ConstantConditions 61 | if (BuildConfig.DEBUG || Log.isLoggable(tag, Log.DEBUG)) { 62 | Log.d(tag, message, cause); 63 | } 64 | } 65 | 66 | public static void LOGV(final String tag, String message) { 67 | //noinspection PointlessBooleanExpression,ConstantConditions 68 | if (BuildConfig.DEBUG && Log.isLoggable(tag, Log.VERBOSE)) { 69 | Log.v(tag, message); 70 | } 71 | } 72 | 73 | public static void LOGV(final String tag, String message, Throwable cause) { 74 | //noinspection PointlessBooleanExpression,ConstantConditions 75 | if (BuildConfig.DEBUG && Log.isLoggable(tag, Log.VERBOSE)) { 76 | Log.v(tag, message, cause); 77 | } 78 | } 79 | 80 | public static void LOGI(final String tag, String message) { 81 | Log.i(tag, message); 82 | } 83 | 84 | public static void LOGI(final String tag, String message, Throwable cause) { 85 | Log.i(tag, message, cause); 86 | } 87 | 88 | public static void LOGW(final String tag, String message) { 89 | if (BuildConfig.DEBUG) { 90 | Log.w(tag, message, new Throwable()); // create a stacktrace 91 | } else { 92 | Log.w(tag, message); 93 | } 94 | } 95 | 96 | public static void LOGW(final String tag, String message, Throwable cause) { 97 | Log.w(tag, message, cause); 98 | } 99 | 100 | public static void LOGE(final String tag, String message) { 101 | if (BuildConfig.DEBUG) { 102 | Log.e(tag, message, new Throwable()); // create a stacktrace 103 | } else { 104 | Log.e(tag, message); 105 | } 106 | } 107 | 108 | public static void LOGE(final String tag, String message, Throwable cause) { 109 | Log.e(tag, message, cause); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/util/MathUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.util; 19 | 20 | import android.util.FloatMath; 21 | 22 | public class MathUtil { 23 | public static float constrain(float min, float max, float v) { 24 | return Math.max(min, Math.min(max, v)); 25 | } 26 | 27 | public static float interpolate(float x1, float x2, float f) { 28 | return x1 + (x2 - x1) * f; 29 | } 30 | 31 | public static float uninterpolate(float x1, float x2, float v) { 32 | if (x2 - x1 == 0) { 33 | throw new IllegalArgumentException("Can't reverse interpolate with domain size of 0"); 34 | } 35 | return (v - x1) / (x2 - x1); 36 | } 37 | 38 | public static float dist(float x, float y) { 39 | return FloatMath.sqrt(x * x + y * y); 40 | } 41 | 42 | public static int floorEven(int num) { 43 | return num & ~0x01; 44 | } 45 | 46 | public static int roundMult4(int num) { 47 | return (num + 2) & ~0x03; 48 | } 49 | 50 | public static boolean isEven(int num) { 51 | return num % 2 == 0; 52 | } 53 | 54 | // divide two integers but round up 55 | // see http://stackoverflow.com/a/7446742/102703 56 | public static int intDivideRoundUp(int num, int divisor) { 57 | int sign = (num > 0 ? 1 : -1) * (divisor > 0 ? 1 : -1); 58 | return sign * (Math.abs(num) + Math.abs(divisor) - 1) / Math.abs(divisor); 59 | } 60 | 61 | private MathUtil() { 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/util/MultiSelectionController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.util; 19 | 20 | import android.os.Bundle; 21 | import android.os.Parcelable; 22 | 23 | import java.util.HashSet; 24 | import java.util.Set; 25 | 26 | /** 27 | * Utilities for storing multiple selection information in collection views. 28 | */ 29 | public class MultiSelectionController { 30 | private String mStateKey; 31 | private Set mSelection = new HashSet<>(); 32 | private Callbacks mCallbacks = DUMMY_CALLBACKS; 33 | 34 | public MultiSelectionController(String stateKey) { 35 | mStateKey = stateKey; 36 | } 37 | 38 | public void restoreInstanceState(Bundle savedInstanceState) { 39 | if (savedInstanceState != null) { 40 | mSelection.clear(); 41 | Parcelable[] selection = savedInstanceState.getParcelableArray(mStateKey); 42 | if (selection != null && selection.length > 0) { 43 | for (Parcelable item : selection) { 44 | mSelection.add((T) item); 45 | } 46 | } 47 | } 48 | 49 | mCallbacks.onSelectionChanged(true, false); 50 | } 51 | 52 | public void saveInstanceState(Bundle outBundle) { 53 | Parcelable[] selection = new Parcelable[mSelection.size()]; 54 | int i = 0; 55 | for (Parcelable item : mSelection) { 56 | selection[i] = item; 57 | ++i; 58 | } 59 | 60 | outBundle.putParcelableArray(mStateKey, selection); 61 | } 62 | 63 | public void setCallbacks(Callbacks callbacks) { 64 | mCallbacks = callbacks; 65 | if (mCallbacks == null) { 66 | mCallbacks = DUMMY_CALLBACKS; 67 | } 68 | } 69 | 70 | public Set getSelection() { 71 | return new HashSet<>(mSelection); 72 | } 73 | 74 | public int getSelectedCount() { 75 | return mSelection.size(); 76 | } 77 | 78 | public boolean isSelecting() { 79 | return mSelection.size() > 0; 80 | } 81 | 82 | public void toggle(T item, boolean fromUser) { 83 | if (mSelection.contains(item)) { 84 | mSelection.remove(item); 85 | } else { 86 | mSelection.add(item); 87 | } 88 | 89 | mCallbacks.onSelectionChanged(false, fromUser); 90 | } 91 | 92 | public void reset(boolean fromUser) { 93 | mSelection.clear(); 94 | mCallbacks.onSelectionChanged(false, fromUser); 95 | } 96 | 97 | public boolean isSelected(T item) { 98 | return mSelection.contains(item); 99 | } 100 | 101 | public static interface Callbacks { 102 | void onSelectionChanged(boolean restored, boolean fromUser); 103 | } 104 | 105 | private static final Callbacks DUMMY_CALLBACKS = new Callbacks() { 106 | @Override 107 | public void onSelectionChanged(boolean restored, boolean fromUser) { 108 | } 109 | }; 110 | } 111 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/util/ObservableHorizontalScrollView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.util; 19 | 20 | import android.content.Context; 21 | import android.util.AttributeSet; 22 | import android.view.MotionEvent; 23 | import android.widget.HorizontalScrollView; 24 | 25 | /** 26 | * A custom ScrollView that can accept a scroll listener. 27 | */ 28 | public class ObservableHorizontalScrollView extends HorizontalScrollView { 29 | private Callbacks mCallbacks; 30 | 31 | public ObservableHorizontalScrollView(Context context, AttributeSet attrs) { 32 | super(context, attrs); 33 | } 34 | 35 | @Override 36 | protected void onScrollChanged(int l, int t, int oldl, int oldt) { 37 | super.onScrollChanged(l, t, oldl, oldt); 38 | if (mCallbacks != null) { 39 | mCallbacks.onScrollChanged(l); 40 | } 41 | } 42 | 43 | @Override 44 | public boolean onTouchEvent(MotionEvent ev) { 45 | if (mCallbacks != null) { 46 | switch (ev.getActionMasked()) { 47 | case MotionEvent.ACTION_DOWN: 48 | mCallbacks.onDownMotionEvent(); 49 | break; 50 | case MotionEvent.ACTION_UP: 51 | case MotionEvent.ACTION_CANCEL: 52 | mCallbacks.onUpOrCancelMotionEvent(); 53 | break; 54 | } 55 | } 56 | return super.onTouchEvent(ev); 57 | } 58 | 59 | @Override 60 | public int computeHorizontalScrollRange() { 61 | return super.computeHorizontalScrollRange(); 62 | } 63 | 64 | public void setCallbacks(Callbacks listener) { 65 | mCallbacks = listener; 66 | } 67 | 68 | public static interface Callbacks { 69 | public void onScrollChanged(int scrollX); 70 | public void onDownMotionEvent(); 71 | public void onUpOrCancelMotionEvent(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/util/ScrimUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.util; 19 | 20 | import android.graphics.Color; 21 | import android.graphics.LinearGradient; 22 | import android.graphics.Shader; 23 | import android.graphics.drawable.Drawable; 24 | import android.graphics.drawable.PaintDrawable; 25 | import android.graphics.drawable.ShapeDrawable; 26 | import android.graphics.drawable.shapes.RectShape; 27 | import android.util.FloatMath; 28 | import android.view.Gravity; 29 | 30 | /** 31 | * Utility methods for creating prettier gradient scrims. 32 | */ 33 | public class ScrimUtil { 34 | private ScrimUtil() { 35 | } 36 | 37 | /** 38 | * Creates an approximated cubic gradient using a multi-stop linear gradient. See 39 | * this post for more 40 | * details. 41 | */ 42 | public static Drawable makeCubicGradientScrimDrawable(int baseColor, int numStops, int gravity) { 43 | numStops = Math.max(numStops, 2); 44 | 45 | PaintDrawable paintDrawable = new PaintDrawable(); 46 | paintDrawable.setShape(new RectShape()); 47 | 48 | final int[] stopColors = new int[numStops]; 49 | 50 | int red = Color.red(baseColor); 51 | int green = Color.green(baseColor); 52 | int blue = Color.blue(baseColor); 53 | int alpha = Color.alpha(baseColor); 54 | 55 | for (int i = 0; i < numStops; i++) { 56 | float x = i * 1f / (numStops - 1); 57 | float opacity = MathUtil.constrain(0, 1, FloatMath.pow(x, 3)); 58 | stopColors[i] = Color.argb((int) (alpha * opacity), red, green, blue); 59 | } 60 | 61 | final float x0, x1, y0, y1; 62 | switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 63 | case Gravity.LEFT: x0 = 1; x1 = 0; break; 64 | case Gravity.RIGHT: x0 = 0; x1 = 1; break; 65 | default: x0 = 0; x1 = 0; break; 66 | } 67 | switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) { 68 | case Gravity.TOP: y0 = 1; y1 = 0; break; 69 | case Gravity.BOTTOM: y0 = 0; y1 = 1; break; 70 | default: y0 = 0; y1 = 0; break; 71 | } 72 | 73 | paintDrawable.setShaderFactory(new ShapeDrawable.ShaderFactory() { 74 | @Override 75 | public Shader resize(int width, int height) { 76 | LinearGradient linearGradient = new LinearGradient( 77 | width * x0, 78 | height * y0, 79 | width * x1, 80 | height * y1, 81 | stopColors, null, 82 | Shader.TileMode.CLAMP); 83 | return linearGradient; 84 | } 85 | }); 86 | 87 | return paintDrawable; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/util/Scrollbar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.util; 19 | 20 | import android.content.Context; 21 | import android.content.res.TypedArray; 22 | import android.graphics.Canvas; 23 | import android.graphics.Paint; 24 | import android.graphics.Path; 25 | import android.graphics.RectF; 26 | import android.util.AttributeSet; 27 | import android.view.View; 28 | 29 | import net.nurik.roman.muzei.R; 30 | 31 | public class Scrollbar extends View { 32 | private boolean mHidden = true; 33 | 34 | private final int mAnimationDuration; 35 | 36 | private int mIndicatorColor = 0xff000000; 37 | private int mBackgroundColor = 0x80000000; 38 | 39 | private float mIndicatorWidth; 40 | private Paint mBackgroundPaint; 41 | private Paint mIndicatorPaint; 42 | 43 | private Path mTempPath = new Path(); 44 | private RectF mTempRectF = new RectF(); 45 | 46 | private int mWidth; 47 | private int mHeight; 48 | 49 | private int mScrollRange; 50 | private int mViewportWidth; 51 | private float mPosition; 52 | 53 | public Scrollbar(Context context) { 54 | this(context, null, 0); 55 | } 56 | 57 | public Scrollbar(Context context, AttributeSet attrs) { 58 | this(context, attrs, 0); 59 | } 60 | 61 | public Scrollbar(Context context, AttributeSet attrs, int defStyle) { 62 | super(context, attrs, defStyle); 63 | 64 | mAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime); 65 | 66 | final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Scrollbar); 67 | mBackgroundColor = a.getColor(R.styleable.Scrollbar_backgroundColor, mBackgroundColor); 68 | mIndicatorColor = a.getColor(R.styleable.Scrollbar_indicatorColor, mIndicatorColor); 69 | a.recycle(); 70 | 71 | mBackgroundPaint = new Paint(); 72 | mBackgroundPaint.setColor(mBackgroundColor); 73 | mBackgroundPaint.setAntiAlias(true); 74 | 75 | mIndicatorPaint = new Paint(); 76 | mIndicatorPaint.setColor(mIndicatorColor); 77 | mIndicatorPaint.setAntiAlias(true); 78 | 79 | setAlpha(0); 80 | } 81 | 82 | @Override 83 | protected void onDraw(Canvas canvas) { 84 | super.onDraw(canvas); 85 | if (mScrollRange <= mViewportWidth) { 86 | return; 87 | } 88 | 89 | mTempRectF.top = 0; 90 | mTempRectF.bottom = mHeight; 91 | mTempRectF.left = 0; 92 | mTempRectF.right = mWidth; 93 | 94 | drawPill(canvas, mTempRectF, mBackgroundPaint); 95 | 96 | mTempRectF.top = 0; 97 | mTempRectF.bottom = mHeight; 98 | mTempRectF.left = mPosition * 1f / (mScrollRange - mViewportWidth) 99 | * mWidth * (1 - mIndicatorWidth); 100 | mTempRectF.right = mTempRectF.left + mIndicatorWidth * mWidth; 101 | 102 | drawPill(canvas, mTempRectF, mIndicatorPaint); 103 | } 104 | 105 | private void drawPill(Canvas canvas, RectF rectF, Paint paint) { 106 | float radius = rectF.height() / 2; 107 | float temp; 108 | 109 | mTempPath.reset(); 110 | mTempPath.moveTo(rectF.left + radius, rectF.top); 111 | mTempPath.lineTo(rectF.right - radius, rectF.top); 112 | 113 | temp = rectF.left; 114 | rectF.left = rectF.right - 2 * radius; 115 | mTempPath.arcTo(rectF, 270, 180); 116 | rectF.left = temp; 117 | 118 | mTempPath.lineTo(rectF.left + radius, rectF.bottom); 119 | 120 | temp = rectF.right; 121 | rectF.right = rectF.left + rectF.height(); 122 | mTempPath.arcTo(rectF, 90, 180); 123 | rectF.right = temp; 124 | 125 | mTempPath.close(); 126 | canvas.drawPath(mTempPath, paint); 127 | } 128 | 129 | @Override 130 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 131 | setMeasuredDimension( 132 | View.resolveSize(0, widthMeasureSpec), 133 | View.resolveSize(0, heightMeasureSpec)); 134 | } 135 | 136 | public void setScrollPosition(int position) { 137 | mPosition = MathUtil.constrain(0, mScrollRange, position); 138 | postInvalidateOnAnimation(); 139 | } 140 | 141 | public void setScrollRangeAndViewportWidth(int scrollRange, int viewportWidth) { 142 | mScrollRange = scrollRange; 143 | mViewportWidth = viewportWidth; 144 | mIndicatorWidth = 0.1f; 145 | if (mScrollRange > 0) { 146 | mIndicatorWidth = MathUtil.constrain(mIndicatorWidth, 1f, 147 | mViewportWidth * 1f / mScrollRange); 148 | } 149 | postInvalidateOnAnimation(); 150 | } 151 | 152 | @Override 153 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 154 | super.onSizeChanged(w, h, oldw, oldh); 155 | mHeight = h; 156 | mWidth = w; 157 | } 158 | 159 | public void show() { 160 | if (!mHidden) { 161 | return; 162 | } 163 | 164 | mHidden = false; 165 | animate().cancel(); 166 | animate().alpha(1f).setDuration(mAnimationDuration); 167 | } 168 | 169 | public void hide() { 170 | if (mHidden) { 171 | return; 172 | } 173 | 174 | mHidden = true; 175 | animate().cancel(); 176 | animate().alpha(0f).setDuration(mAnimationDuration); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/util/SelectionBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /* 19 | * Modifications: 20 | * -Imported from AOSP frameworks/base/core/java/com/android/internal/content 21 | * -Changed package name 22 | */ 23 | 24 | package com.google.android.apps.muzei.util; 25 | 26 | import android.content.ContentValues; 27 | import android.database.Cursor; 28 | import android.database.sqlite.SQLiteDatabase; 29 | import android.text.TextUtils; 30 | 31 | import java.util.ArrayList; 32 | import java.util.Arrays; 33 | import java.util.Collections; 34 | import java.util.HashMap; 35 | import java.util.Map; 36 | 37 | import static com.google.android.apps.muzei.util.LogUtil.LOGV; 38 | 39 | /** 40 | * Helper for building selection clauses for {@link android.database.sqlite.SQLiteDatabase}. Each 41 | * appended clause is combined using {@code AND}. This class is not 42 | * thread safe. 43 | */ 44 | public class SelectionBuilder { 45 | private static final String TAG = LogUtil.makeLogTag(SelectionBuilder.class); 46 | 47 | private String mTable = null; 48 | private Map mProjectionMap = new HashMap<>(); 49 | private StringBuilder mSelection = new StringBuilder(); 50 | private ArrayList mSelectionArgs = new ArrayList<>(); 51 | 52 | /** 53 | * Reset any internal state, allowing this builder to be recycled. 54 | */ 55 | public SelectionBuilder reset() { 56 | mTable = null; 57 | mSelection.setLength(0); 58 | mSelectionArgs.clear(); 59 | return this; 60 | } 61 | 62 | /** 63 | * Append the given selection clause to the internal state. Each clause is 64 | * surrounded with parenthesis and combined using {@code AND}. 65 | */ 66 | public SelectionBuilder where(String selection, String... selectionArgs) { 67 | if (TextUtils.isEmpty(selection)) { 68 | if (selectionArgs != null && selectionArgs.length > 0) { 69 | throw new IllegalArgumentException( 70 | "Valid selection required when including arguments="); 71 | } 72 | 73 | // Shortcut when clause is empty 74 | return this; 75 | } 76 | 77 | if (mSelection.length() > 0) { 78 | mSelection.append(" AND "); 79 | } 80 | 81 | mSelection.append("(").append(selection).append(")"); 82 | if (selectionArgs != null) { 83 | Collections.addAll(mSelectionArgs, selectionArgs); 84 | } 85 | 86 | return this; 87 | } 88 | 89 | public SelectionBuilder table(String table) { 90 | mTable = table; 91 | return this; 92 | } 93 | 94 | private void assertTable() { 95 | if (mTable == null) { 96 | throw new IllegalStateException("Table not specified"); 97 | } 98 | } 99 | 100 | public SelectionBuilder mapToTable(String column, String table) { 101 | mProjectionMap.put(column, table + "." + column); 102 | return this; 103 | } 104 | 105 | public SelectionBuilder map(String fromColumn, String toClause) { 106 | mProjectionMap.put(fromColumn, toClause + " AS " + fromColumn); 107 | return this; 108 | } 109 | 110 | /** 111 | * Return selection string for current internal state. 112 | * 113 | * @see #getSelectionArgs() 114 | */ 115 | public String getSelection() { 116 | return mSelection.toString(); 117 | } 118 | 119 | /** 120 | * Return selection arguments for current internal state. 121 | * 122 | * @see #getSelection() 123 | */ 124 | public String[] getSelectionArgs() { 125 | return mSelectionArgs.toArray(new String[mSelectionArgs.size()]); 126 | } 127 | 128 | private void mapColumns(String[] columns) { 129 | for (int i = 0; i < columns.length; i++) { 130 | final String target = mProjectionMap.get(columns[i]); 131 | if (target != null) { 132 | columns[i] = target; 133 | } 134 | } 135 | } 136 | 137 | @Override 138 | public String toString() { 139 | return "SelectionBuilder[table=" + mTable + ", selection=" + getSelection() 140 | + ", selectionArgs=" + Arrays.toString(getSelectionArgs()) + "]"; 141 | } 142 | 143 | /** 144 | * Execute query using the current internal state as {@code WHERE} clause. 145 | */ 146 | public Cursor query(SQLiteDatabase db, String[] columns, String orderBy) { 147 | return query(db, columns, null, null, orderBy, null); 148 | } 149 | 150 | /** 151 | * Execute query using the current internal state as {@code WHERE} clause. 152 | */ 153 | public Cursor query(SQLiteDatabase db, String[] columns, String groupBy, 154 | String having, String orderBy, String limit) { 155 | assertTable(); 156 | if (columns != null) mapColumns(columns); 157 | LOGV(TAG, "query(columns=" + Arrays.toString(columns) + ") " + this); 158 | return db.query(mTable, columns, getSelection(), getSelectionArgs(), groupBy, having, 159 | orderBy, limit); 160 | } 161 | 162 | /** 163 | * Execute update using the current internal state as {@code WHERE} clause. 164 | */ 165 | public int update(SQLiteDatabase db, ContentValues values) { 166 | assertTable(); 167 | LOGV(TAG, "update() " + this); 168 | return db.update(mTable, values, getSelection(), getSelectionArgs()); 169 | } 170 | 171 | /** 172 | * Execute delete using the current internal state as {@code WHERE} clause. 173 | */ 174 | public int delete(SQLiteDatabase db) { 175 | assertTable(); 176 | LOGV(TAG, "delete() " + this); 177 | return db.delete(mTable, getSelection(), getSelectionArgs()); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/util/ShadowDipsTextView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.util; 19 | 20 | import android.content.Context; 21 | import android.content.res.TypedArray; 22 | import android.util.AttributeSet; 23 | import android.widget.TextView; 24 | 25 | import net.nurik.roman.muzei.R; 26 | 27 | 28 | public class ShadowDipsTextView extends TextView { 29 | public ShadowDipsTextView(Context context) { 30 | super(context); 31 | init(context, null, 0); 32 | } 33 | 34 | public ShadowDipsTextView(Context context, AttributeSet attrs) { 35 | super(context, attrs); 36 | init(context, attrs, 0); 37 | } 38 | 39 | public ShadowDipsTextView(Context context, AttributeSet attrs, int defStyle) { 40 | super(context, attrs, defStyle); 41 | init(context, attrs, defStyle); 42 | } 43 | 44 | private void init(Context context, AttributeSet attrs, int defStyle) { 45 | final TypedArray a = context.obtainStyledAttributes(attrs, 46 | R.styleable.ShadowDipsTextView, defStyle, 0); 47 | int shadowDx = a.getDimensionPixelSize(R.styleable.ShadowDipsTextView_shadowDx, 0); 48 | int shadowDy = a.getDimensionPixelSize(R.styleable.ShadowDipsTextView_shadowDy, 0); 49 | int shadowRadius = a.getDimensionPixelSize(R.styleable.ShadowDipsTextView_shadowRadius, 0); 50 | int shadowColor = a.getColor(R.styleable.ShadowDipsTextView_shadowColor, 0); 51 | if (shadowColor != 0) { 52 | setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor); 53 | } 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/util/TickingFloatAnimator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.util; 19 | 20 | import android.animation.TimeInterpolator; 21 | import android.os.SystemClock; 22 | import android.view.animation.AccelerateDecelerateInterpolator; 23 | 24 | // Non thread-safe 25 | public class TickingFloatAnimator { 26 | private float mStartValue = 0; 27 | private float mCurrentValue; 28 | private float mEndValue; 29 | private boolean mRunning = false; 30 | private long mStartTime; 31 | private int mDuration = 1000; 32 | private Runnable mEndCallback; 33 | private TimeInterpolator mInterpolator = new AccelerateDecelerateInterpolator(); 34 | 35 | public static TickingFloatAnimator create() { 36 | return new TickingFloatAnimator(); 37 | } 38 | 39 | public TickingFloatAnimator from(float startValue) { 40 | cancel(); 41 | mStartValue = startValue; 42 | mCurrentValue = startValue; 43 | return this; 44 | } 45 | 46 | public TickingFloatAnimator to(float endValue) { 47 | mEndValue = endValue; 48 | return this; 49 | } 50 | 51 | public TickingFloatAnimator withDuration(int duration) { 52 | mDuration = Math.max(0, duration); 53 | return this; 54 | } 55 | 56 | public TickingFloatAnimator withInterpolator(TimeInterpolator interpolator) { 57 | mInterpolator = interpolator; 58 | return this; 59 | } 60 | 61 | public TickingFloatAnimator withEndListener(Runnable listener) { 62 | mEndCallback = listener; 63 | return this; 64 | } 65 | 66 | public void cancel() { 67 | mRunning = false; 68 | mEndValue = mCurrentValue; 69 | } 70 | 71 | public boolean tick() { 72 | if (!mRunning) { 73 | return false; 74 | } 75 | 76 | float t; 77 | if (mDuration <= 0) { 78 | t = 1; 79 | } else { 80 | t = (float) (SystemClock.elapsedRealtime() - mStartTime) * 1f / mDuration; 81 | if (t >= 1) { 82 | t = 1; 83 | } 84 | } 85 | 86 | if (t >= 1) { 87 | // Ended 88 | mRunning = false; 89 | mCurrentValue = mEndValue; 90 | if (mEndCallback != null) { 91 | mEndCallback.run(); 92 | } 93 | return false; 94 | } 95 | 96 | // Still running; compute value 97 | mCurrentValue = mStartValue + mInterpolator.getInterpolation(t) * (mEndValue - mStartValue); 98 | mRunning = true; 99 | return true; 100 | } 101 | 102 | public void start() { 103 | mRunning = true; 104 | mStartValue = mCurrentValue; 105 | mStartTime = SystemClock.elapsedRealtime(); 106 | tick(); 107 | } 108 | 109 | public boolean isRunning() { 110 | return mRunning; 111 | } 112 | 113 | public float currentValue() { 114 | return mCurrentValue; 115 | } 116 | 117 | private TickingFloatAnimator() { 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/util/TypefaceUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.util; 19 | 20 | import android.content.Context; 21 | import android.graphics.Typeface; 22 | 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | 26 | // See https://code.google.com/p/android/issues/detail?id=9904 27 | public class TypefaceUtil { 28 | private static final Map sTypefaceCache = new HashMap<>(); 29 | 30 | public static Typeface getAndCache(Context context, String assetPath) { 31 | synchronized (sTypefaceCache) { 32 | if (!sTypefaceCache.containsKey(assetPath)) { 33 | Typeface tf = Typeface.createFromAsset( 34 | context.getApplicationContext().getAssets(), assetPath); 35 | sTypefaceCache.put(assetPath, tf); 36 | } 37 | return sTypefaceCache.get(assetPath); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /main/src/main/java/com/google/android/apps/muzei/util/Zoomer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Chris Lacy 3 | * Copyright 2014 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.google.android.apps.muzei.util; 19 | 20 | import android.content.Context; 21 | import android.os.SystemClock; 22 | import android.view.animation.DecelerateInterpolator; 23 | import android.view.animation.Interpolator; 24 | 25 | /** 26 | * A simple class that animates double-touch zoom gestures. Functionally similar to a {@link 27 | * android.widget.Scroller}. 28 | */ 29 | class Zoomer { 30 | /** 31 | * The interpolator, used for making zooms animate 'naturally.' 32 | */ 33 | private Interpolator mInterpolator; 34 | 35 | /** 36 | * The total animation duration for a zoom. 37 | */ 38 | private int mAnimationDurationMillis; 39 | 40 | /** 41 | * Whether or not the current zoom has finished. 42 | */ 43 | private boolean mFinished = true; 44 | 45 | /** 46 | * The starting zoom value. 47 | */ 48 | private float mStartZoom = 1f; 49 | 50 | /** 51 | * The current zoom value; computed by {@link #computeZoom()}. 52 | */ 53 | private float mCurrentZoom; 54 | 55 | /** 56 | * The time the zoom started, computed using {@link android.os.SystemClock#elapsedRealtime()}. 57 | */ 58 | private long mStartRTC; 59 | 60 | /** 61 | * The destination zoom factor. 62 | */ 63 | private float mEndZoom; 64 | 65 | public Zoomer(Context context) { 66 | mInterpolator = new DecelerateInterpolator(); 67 | mAnimationDurationMillis = context.getResources().getInteger( 68 | android.R.integer.config_shortAnimTime); 69 | } 70 | 71 | /** 72 | * Forces the zoom finished state to the given value. Unlike {@link #abortAnimation()}, the 73 | * current zoom value isn't set to the ending value. 74 | * 75 | * @see android.widget.Scroller#forceFinished(boolean) 76 | */ 77 | public void forceFinished(boolean finished) { 78 | mFinished = finished; 79 | } 80 | 81 | /** 82 | * Aborts the animation, setting the current zoom value to the ending value. 83 | * 84 | * @see android.widget.Scroller#abortAnimation() 85 | */ 86 | public void abortAnimation() { 87 | mFinished = true; 88 | mCurrentZoom = mEndZoom; 89 | } 90 | 91 | /** 92 | * Starts a zoom from startZoom to endZoom. That is, to zoom from 100% to 125%, endZoom should 93 | * by 0.25f. 94 | * 95 | * @see android.widget.Scroller#startScroll(int, int, int, int) 96 | */ 97 | public void startZoom(float startZoom, float endZoom) { 98 | mStartRTC = SystemClock.elapsedRealtime(); 99 | mEndZoom = endZoom; 100 | 101 | mFinished = false; 102 | mStartZoom = startZoom; 103 | mCurrentZoom = startZoom; 104 | } 105 | 106 | /** 107 | * Computes the current zoom level, returning true if the zoom is still active and false if the 108 | * zoom has finished. 109 | * 110 | * @see android.widget.Scroller#computeScrollOffset() 111 | */ 112 | public boolean computeZoom() { 113 | if (mFinished) { 114 | return false; 115 | } 116 | 117 | long tRTC = SystemClock.elapsedRealtime() - mStartRTC; 118 | if (tRTC >= mAnimationDurationMillis) { 119 | mFinished = true; 120 | mCurrentZoom = mEndZoom; 121 | return false; 122 | } 123 | 124 | float t = tRTC * 1f / mAnimationDurationMillis; 125 | mCurrentZoom = MathUtil.interpolate( 126 | mStartZoom, mEndZoom, mInterpolator.getInterpolation(t)); 127 | return true; 128 | } 129 | 130 | /** 131 | * Returns the current zoom level. 132 | * 133 | * @see android.widget.Scroller#getCurrX() 134 | */ 135 | public float getCurrZoom() { 136 | return mCurrentZoom; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /main/src/main/java/net/rbgrn/android/glwallpaperservice/BaseConfigChooser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.rbgrn.android.glwallpaperservice; 18 | 19 | import android.opengl.GLSurfaceView; 20 | 21 | import javax.microedition.khronos.egl.EGL10; 22 | import javax.microedition.khronos.egl.EGLConfig; 23 | import javax.microedition.khronos.egl.EGLDisplay; 24 | 25 | /** 26 | * Created by romannurik on 11/6/13. 27 | */ 28 | abstract class BaseConfigChooser implements GLSurfaceView.EGLConfigChooser { 29 | private int eglContextClientVersion; 30 | 31 | public BaseConfigChooser(int[] configSpec, int eglContextClientVersion) { 32 | this.eglContextClientVersion = eglContextClientVersion; 33 | mConfigSpec = filterConfigSpec(configSpec); 34 | } 35 | 36 | public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { 37 | int[] num_config = new int[1]; 38 | if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, 39 | num_config)) { 40 | throw new IllegalArgumentException("eglChooseConfig failed"); 41 | } 42 | 43 | int numConfigs = num_config[0]; 44 | 45 | if (numConfigs <= 0) { 46 | throw new IllegalArgumentException( 47 | "No configs match configSpec"); 48 | } 49 | 50 | EGLConfig[] configs = new EGLConfig[numConfigs]; 51 | if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, 52 | num_config)) { 53 | throw new IllegalArgumentException("eglChooseConfig#2 failed"); 54 | } 55 | EGLConfig config = chooseConfig(egl, display, configs); 56 | if (config == null) { 57 | throw new IllegalArgumentException("No config chosen"); 58 | } 59 | return config; 60 | } 61 | 62 | abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, 63 | EGLConfig[] configs); 64 | 65 | protected int[] mConfigSpec; 66 | 67 | private int[] filterConfigSpec(int[] configSpec) { 68 | if (eglContextClientVersion != 2) { 69 | return configSpec; 70 | } 71 | /* We know none of the subclasses define EGL_RENDERABLE_TYPE. 72 | * And we know the configSpec is well formed. 73 | */ 74 | int len = configSpec.length; 75 | int[] newConfigSpec = new int[len + 2]; 76 | System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1); 77 | newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE; 78 | newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */ 79 | newConfigSpec[len+1] = EGL10.EGL_NONE; 80 | return newConfigSpec; 81 | } 82 | 83 | public static class ComponentSizeChooser extends BaseConfigChooser { 84 | public ComponentSizeChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, 85 | int stencilSize, int eglContextClientVersion) { 86 | super(new int[] { EGL10.EGL_RED_SIZE, redSize, EGL10.EGL_GREEN_SIZE, greenSize, EGL10.EGL_BLUE_SIZE, 87 | blueSize, EGL10.EGL_ALPHA_SIZE, alphaSize, EGL10.EGL_DEPTH_SIZE, depthSize, EGL10.EGL_STENCIL_SIZE, 88 | stencilSize, EGL10.EGL_NONE }, eglContextClientVersion); 89 | mValue = new int[1]; 90 | mRedSize = redSize; 91 | mGreenSize = greenSize; 92 | mBlueSize = blueSize; 93 | mAlphaSize = alphaSize; 94 | mDepthSize = depthSize; 95 | mStencilSize = stencilSize; 96 | } 97 | 98 | @Override 99 | public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) { 100 | EGLConfig closestConfig = null; 101 | int closestDistance = 1000; 102 | for (EGLConfig config : configs) { 103 | int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0); 104 | int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0); 105 | if (d >= mDepthSize && s >= mStencilSize) { 106 | int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0); 107 | int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0); 108 | int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0); 109 | int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0); 110 | int distance = Math.abs(r - mRedSize) + Math.abs(g - mGreenSize) + Math.abs(b - mBlueSize) 111 | + Math.abs(a - mAlphaSize); 112 | if (distance < closestDistance) { 113 | closestDistance = distance; 114 | closestConfig = config; 115 | } 116 | } 117 | } 118 | return closestConfig; 119 | } 120 | 121 | private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, int attribute, int defaultValue) { 122 | 123 | if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { 124 | return mValue[0]; 125 | } 126 | return defaultValue; 127 | } 128 | 129 | private int[] mValue; 130 | // Subclasses can adjust these values: 131 | protected int mRedSize; 132 | protected int mGreenSize; 133 | protected int mBlueSize; 134 | protected int mAlphaSize; 135 | protected int mDepthSize; 136 | protected int mStencilSize; 137 | } 138 | 139 | /** 140 | * This class will choose a supported surface as close to RGB565 as possible, with or without a depth buffer. 141 | * 142 | */ 143 | public static class SimpleEGLConfigChooser extends ComponentSizeChooser { 144 | public SimpleEGLConfigChooser(boolean withDepthBuffer, int eglContextClientVersion) { 145 | super(4, 4, 4, 0, withDepthBuffer ? 16 : 0, 0, eglContextClientVersion); 146 | // Adjust target values. This way we'll accept a 4444 or 147 | // 555 buffer if there's no 565 buffer available. 148 | mRedSize = 5; 149 | mGreenSize = 6; 150 | mBlueSize = 5; 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /main/src/main/res/animator/fade_in.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 24 | -------------------------------------------------------------------------------- /main/src/main/res/animator/fade_out.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 24 | -------------------------------------------------------------------------------- /main/src/main/res/drawable-hdpi/ic_notif_full_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-hdpi/ic_notif_full_info.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-hdpi/ic_notif_full_next_artwork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-hdpi/ic_notif_full_next_artwork.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-hdpi/ic_notif_full_user_command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-hdpi/ic_notif_full_user_command.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-nodpi/ic_source_featured.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-nodpi/ic_source_featured.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-nodpi/ic_source_gallery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-nodpi/ic_source_gallery.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-nodpi/ic_source_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-nodpi/ic_source_selected.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-nodpi/thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-nodpi/thumb.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-v21/grey_selectable_item_background_circle.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /main/src/main/res/drawable-v21/settings_source_item_image_overlay.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /main/src/main/res/drawable-v21/white_selectable_item_background.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /main/src/main/res/drawable-xhdpi/ic_stat_muzei.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xhdpi/ic_stat_muzei.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/gallery_settings_chosen_photo_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/gallery_settings_chosen_photo_selected.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/grumpy_mcpuzzles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/grumpy_mcpuzzles.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/ic_ab_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/ic_ab_done.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/ic_ab_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/ic_ab_up.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/ic_action_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/ic_action_play.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/ic_action_remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/ic_action_remove.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/ic_action_rotate_interval.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/ic_action_rotate_interval.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/ic_add_photos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/ic_add_photos.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/ic_notif_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/ic_notif_info.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/ic_notif_next_artwork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/ic_notif_next_artwork.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/ic_overflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/ic_overflow.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/ic_skip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/ic_skip.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/ic_source_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/ic_source_settings.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/ic_stat_muzei.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/ic_stat_muzei.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/logo_subtitle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/logo_subtitle.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/scrubber_control_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/scrubber_control_disabled.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/scrubber_control_focused.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/scrubber_control_focused.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/scrubber_control_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/scrubber_control_normal.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/scrubber_control_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/scrubber_control_pressed.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/scrubber_primary.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/scrubber_primary.9.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/scrubber_secondary.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/scrubber_secondary.9.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/scrubber_track.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/scrubber_track.9.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/scrubber_track_blur_amount.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/scrubber_track_blur_amount.9.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/scrubber_track_dim_amount.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/scrubber_track_dim_amount.9.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/scrubber_track_grey_amount.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/scrubber_track_grey_amount.9.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/spinner_triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/spinner_triangle.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/tutorial_icon_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/tutorial_icon_off.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/tutorial_icon_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/tutorial_icon_on.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/white_item_focused.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/white_item_focused.9.png -------------------------------------------------------------------------------- /main/src/main/res/drawable-xxhdpi/white_item_pressed.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrislacy/ActionLauncherApi/22ad8455c1a31d6117223a1975d982e92aa416ef/main/src/main/res/drawable-xxhdpi/white_item_pressed.9.png -------------------------------------------------------------------------------- /main/src/main/res/drawable/grey_selectable_item_background_circle.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /main/src/main/res/drawable/intro_background_protection.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /main/src/main/res/drawable/metadata_scrim.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /main/src/main/res/drawable/popup_background.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /main/src/main/res/drawable/scrubber_control_selector.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /main/src/main/res/drawable/scrubber_progress_blur_amount.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /main/src/main/res/drawable/scrubber_progress_dim_amount.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /main/src/main/res/drawable/scrubber_progress_grey_amount.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /main/src/main/res/drawable/scrubber_progress_horizontal.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 21 | 22 | 24 | 25 | 26 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /main/src/main/res/drawable/settings_source_item_image_overlay.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /main/src/main/res/drawable/statusbar_scrim.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /main/src/main/res/drawable/tutorial_icon.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /main/src/main/res/drawable/white_circle_button.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /main/src/main/res/drawable/white_selectable_item_background.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /main/src/main/res/layout/muzei_activity.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 24 | 25 | 29 | 30 |