diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index e96cda9b..45b56541 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,43 +1,12 @@ - - - - - - - - - - - - - - - + diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7f..85849833 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,9 @@ + + + + \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 7bf07527..4dea9615 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,28 +2,41 @@ language: android android: components: - - build-tools-28.0.3 - - android-28 + - build-tools-29.0.3 + - android-29 - extra licenses: - 'android-sdk-license-.+' - 'google-gdk-license-.+' env: - - TRAVIS_NODE_VERSION=10 RUST_TOOLCHAIN=nightly + global: + - TRAVIS_NODE_VERSION=10 \ + - RUST_TOOLCHAIN=nightly \ + - SUPPLY_TRACK=production # used by fastlane to determine track to publish to before_install: - - yes | sdkmanager "platforms;android-27" + # RELEASE is used by `make aw-server-rust` to determine build profile + - export RELEASE=$([ $TRAVIS_TAG ] && echo "true" || echo "false") + + # Install Android SDK/NDK + - yes | sdkmanager "platforms;android-29" - yes | sdkmanager "ndk-bundle" + + # Setup Node to build aw-webui - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION; + + # Install fastlane - gem update --system && gem install bundle && bundle install + + # Setup Rust and NDK and Rust - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $RUST_TOOLCHAIN && source $HOME/.cargo/env - export ANDROID_NDK_HOME=$ANDROID_HOME/ndk-bundle - - ./scripts/setup-rust-with-ndk.sh + - ./aw-server-rust/install-ndk.sh install: # Build release build straight away if on a tagged commit, to avoid building twice - - env RELEASE=$([ $TRAVIS_TAG ] && echo "true" || echo "false") make aw-server-rust + - env make aw-server-rust script: - bundle exec fastlane test @@ -36,13 +49,13 @@ before_deploy: - env RELEASE=true make aw-server-rust # RUSTFLAGS=-g # The RUSTFLAGS=-g is to keep debug symbols in the build, bloats the binary from 7M -> 60MB. Currently using profile.release.debug in Cargo.toml however as -g didn't work on the first try - make aw-webui - sed -i "s/versionName \".*\"/versionName \"$(echo $TRAVIS_TAG | tail -c +2 -)\"/g" mobile/build.gradle # Sets versionName, tail used to skip "v" at start of tag name - - env SUPPLY_TRACK=production bundle exec fastlane update_version + - bundle exec fastlane update_version - bash scripts/build_apk.sh deploy: # Production version - provider: script - script: bundle exec fastlane supply run --apk aw-android*.apk --track production + script: bundle exec fastlane supply run --apk aw-android*.apk skip_cleanup: true on: tags: true diff --git a/Gemfile.lock b/Gemfile.lock index 1519593f..4dea4d75 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,58 +1,75 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.0) - addressable (2.6.0) - public_suffix (>= 2.0.2, < 4.0) + CFPropertyList (3.0.2) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) atomos (0.1.3) - babosa (1.0.2) - claide (1.0.2) + aws-eventstream (1.1.0) + aws-partitions (1.308.0) + aws-sdk-core (3.94.0) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.239.0) + aws-sigv4 (~> 1.1) + jmespath (~> 1.0) + aws-sdk-kms (1.30.0) + aws-sdk-core (~> 3, >= 3.71.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.63.0) + aws-sdk-core (~> 3, >= 3.83.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.1) + aws-sigv4 (1.1.3) + aws-eventstream (~> 1.0, >= 1.0.2) + babosa (1.0.3) + claide (1.0.3) colored (1.2) colored2 (3.1.2) commander-fastlane (4.4.6) highline (~> 1.7.2) declarative (0.0.10) declarative-option (0.1.0) - digest-crc (0.4.1) - domain_name (0.5.20180417) + digest-crc (0.5.1) + domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.2) + dotenv (2.7.5) emoji_regex (1.0.1) - excon (0.63.0) - faraday (0.15.4) + excon (0.73.0) + faraday (0.17.3) multipart-post (>= 1.2, < 3) faraday-cookie_jar (0.0.6) faraday (>= 0.7.4) http-cookie (~> 1.0.0) faraday_middleware (0.13.1) faraday (>= 0.7.4, < 1.0) - fastimage (2.1.5) - fastlane (2.120.0) + fastimage (2.1.7) + fastlane (2.146.1) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.3, < 3.0.0) + aws-sdk-s3 (~> 1.0) babosa (>= 1.0.2, < 2.0.0) bundler (>= 1.12.0, < 3.0.0) colored commander-fastlane (>= 4.4.6, < 5.0.0) dotenv (>= 2.1.1, < 3.0.0) emoji_regex (>= 0.1, < 2.0) - excon (>= 0.45.0, < 1.0.0) - faraday (~> 0.9) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 0.17) faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 0.9) + faraday_middleware (~> 0.13.1) fastimage (>= 2.1.0, < 3.0.0) gh_inspector (>= 1.1.2, < 2.0.0) - google-api-client (>= 0.21.2, < 0.24.0) + google-api-client (>= 0.29.2, < 0.37.0) google-cloud-storage (>= 1.15.0, < 2.0.0) highline (>= 1.7.2, < 2.0.0) json (< 3.0.0) - mini_magick (~> 4.5.1) - multi_json + jwt (~> 2.1.0) + mini_magick (>= 4.9.4, < 5.0.0) multi_xml (~> 0.5) multipart-post (~> 2.0.0) plist (>= 3.1.0, < 4.0.0) public_suffix (~> 2.0.0) - rubyzip (>= 1.2.2, < 2.0.0) + rubyzip (>= 1.3.0, < 2.0.0) security (= 0.1.3) simctl (~> 1.6.3) slack-notifier (>= 2.0.0, < 3.0.0) @@ -61,52 +78,55 @@ GEM tty-screen (>= 0.6.3, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) - xcodeproj (>= 1.8.1, < 2.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) fastlane-plugin-increment_version_code (0.4.3) gh_inspector (1.1.3) - google-api-client (0.23.9) + google-api-client (0.36.4) addressable (~> 2.5, >= 2.5.1) - googleauth (>= 0.5, < 0.7.0) + googleauth (~> 0.9) httpclient (>= 2.8.1, < 3.0) - mime-types (~> 3.0) + mini_mime (~> 1.0) representable (~> 3.0) retriable (>= 2.0, < 4.0) - signet (~> 0.9) - google-cloud-core (1.3.0) + signet (~> 0.12) + google-cloud-core (1.5.0) google-cloud-env (~> 1.0) - google-cloud-env (1.0.5) - faraday (~> 0.11) - google-cloud-storage (1.16.0) + google-cloud-errors (~> 1.0) + google-cloud-env (1.3.1) + faraday (>= 0.17.3, < 2.0) + google-cloud-errors (1.0.0) + google-cloud-storage (1.26.0) + addressable (~> 2.5) digest-crc (~> 0.4) - google-api-client (~> 0.23) + google-api-client (~> 0.33) google-cloud-core (~> 1.2) - googleauth (>= 0.6.2, < 0.10.0) - googleauth (0.6.7) - faraday (~> 0.12) + googleauth (~> 0.9) + mini_mime (~> 1.0) + googleauth (0.12.0) + faraday (>= 0.17.3, < 2.0) jwt (>= 1.4, < 3.0) memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) - signet (~> 0.7) + signet (~> 0.14) highline (1.7.10) http-cookie (1.0.3) domain_name (~> 0.5) httpclient (2.8.3) - json (2.2.0) + jmespath (1.4.0) + json (2.3.0) jwt (2.1.0) - memoist (0.16.0) - mime-types (3.2.2) - mime-types-data (~> 3.2015) - mime-types-data (3.2019.0331) - mini_magick (4.5.1) - multi_json (1.13.1) + memoist (0.16.2) + mini_magick (4.10.1) + mini_mime (1.0.2) + multi_json (1.14.1) multi_xml (0.6.0) multipart-post (2.0.0) nanaimo (0.2.6) naturally (2.2.0) - os (1.0.0) + os (1.1.0) plist (3.5.0) public_suffix (2.0.5) representable (3.0.4) @@ -115,31 +135,31 @@ GEM uber (< 0.2.0) retriable (3.1.2) rouge (2.0.7) - rubyzip (1.2.2) + rubyzip (1.3.0) security (0.1.3) - signet (0.11.0) + signet (0.14.0) addressable (~> 2.3) - faraday (~> 0.9) + faraday (>= 0.17.3, < 2.0) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - simctl (1.6.5) + simctl (1.6.8) CFPropertyList naturally slack-notifier (2.3.2) terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - tty-cursor (0.6.1) - tty-screen (0.6.5) - tty-spinner (0.9.0) - tty-cursor (~> 0.6.0) + tty-cursor (0.7.1) + tty-screen (0.7.1) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) uber (0.1.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.5) - unicode-display_width (1.5.0) + unf_ext (0.0.7.7) + unicode-display_width (1.7.0) word_wrap (1.0.0) - xcodeproj (1.8.2) + xcodeproj (1.16.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) @@ -158,4 +178,4 @@ DEPENDENCIES fastlane-plugin-increment_version_code BUNDLED WITH - 2.0.1 + 2.1.0 diff --git a/Makefile b/Makefile index d2c31d3e..222d7ddf 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,82 @@ -.PHONY: aw-server-rust aw-webui - -aw-server-rust: - cd aw-server-rust && env RUSTFLAGS="-C debuginfo=2" bash compile-android.sh # RUSTFLAGS="-C debuginfo=2" is to keep debug symbols, even in release builds (later stripped by gradle on production builds, non-stripped versions needed for stack resymbolizing with ndk-stack) - mkdir -p mobile/src/main/jniLibs/arm64-v8a/ - ln -sfnv $$(pwd)/aw-server-rust/target/aarch64-linux-android/$$($$RELEASE && echo 'release' || echo 'debug')/libaw_server.so \ - mobile/src/main/jniLibs/arm64-v8a/libaw_server.so - mkdir -p mobile/src/main/jniLibs/x86/ - ln -sfnv $$(pwd)/aw-server-rust/target/i686-linux-android/$$($$RELEASE && echo 'release' || echo 'debug')/libaw_server.so \ - mobile/src/main/jniLibs/x86/libaw_server.so - mkdir -p mobile/src/main/jniLibs/x86_64/ - ln -sfnv $$(pwd)/aw-server-rust/target/x86_64-linux-android/$$($$RELEASE && echo 'release' || echo 'debug')/libaw_server.so \ - mobile/src/main/jniLibs/x86_64/libaw_server.so - ls -lL mobile/src/main/jniLibs/*/* - -aw-webui: - make --directory=aw-server-rust/aw-webui build +.PHONY: aw-webui +SHELL := /bin/bash + +# We should probably do this the "Android way" (would also help with getting it on FDroid): +# - https://developer.android.com/studio/projects/gradle-external-native-builds +# - https://developer.android.com/ndk/guides/android_mk + +RELEASE_TYPE = $(shell $$RELEASE && echo 'release' || echo 'debug') + +# Main targets +all: aw-server-rust aw-webui +build: all + + +# aw-server-rust stuff + +RS_SRCDIR := aw-server-rust +RS_OUTDIR := $(JNILIBS) +RS_SOURCES := $(shell find $(RS_SRCDIR)/aw-* -type f -name '*.rs') + +JNILIBS := mobile/src/main/jniLibs +JNI_arm8 := $(JNILIBS)/arm64-v8a +JNI_arm7 := $(JNILIBS)/armeabi-v7a +JNI_x86 := $(JNILIBS)/x86 +JNI_x64 := $(JNILIBS)/x86_64 + +TARGET := aw-server-rust/target +TARGET_arm7 := $(TARGET)/armv7-linux-androideabi +TARGET_arm8 := $(TARGET)/aarch64-linux-android +TARGET_x64 := $(TARGET)/x86_64-linux-android +TARGET_x86 := $(TARGET)/i686-linux-android + +aw-server-rust: $(JNILIBS) + +.PHONY: $(JNILIBS) +$(JNILIBS): $(JNI_arm7)/libaw_server.so $(JNI_arm8)/libaw_server.so $(JNI_x86)/libaw_server.so $(JNI_x64)/libaw_server.so + ls -lL $@/*/* # Check that symlinks are valid + +# There must be a better way to do this without repeating almost the same rule over and over? +$(JNI_arm7)/libaw_server.so: $(TARGET_arm7)/$(RELEASE_TYPE)/libaw_server.so + mkdir -p $$(dirname $@) + ln -sfnv $$(pwd)/$^ $@ +$(JNI_arm8)/libaw_server.so: $(TARGET_arm8)/$(RELEASE_TYPE)/libaw_server.so + mkdir -p $$(dirname $@) + ln -sfnv $$(pwd)/$^ $@ +$(JNI_x86)/libaw_server.so: $(TARGET_x86)/$(RELEASE_TYPE)/libaw_server.so + mkdir -p $$(dirname $@) + ln -sfnv $$(pwd)/$^ $@ +$(JNI_x64)/libaw_server.so: $(TARGET_x64)/$(RELEASE_TYPE)/libaw_server.so + mkdir -p $$(dirname $@) + ln -sfnv $$(pwd)/$^ $@ + +# This target runs multiple times because it's matched multiple times, not sure how to fix +$(RS_SRCDIR)/target/%/$(RELEASE_TYPE)/libaw_server.so: $(RS_SOURCES) + echo $@ + cd aw-server-rust && env RUSTFLAGS="-C debuginfo=2 -Awarnings" bash compile-android.sh +# Explanation of RUSTFLAGS: +# `-Awarnings` allows all warnings, for cleaner output (warnings should be detected in aw-server-rust CI anyway) +# `-C debuginfo=2` is to keep debug symbols, even in release builds (later stripped by gradle on production builds, non-stripped versions needed for stack resymbolizing with ndk-stack) + + +# aw-webui + +WEBUI_SRCDIR := aw-server-rust/aw-webui +WEBUI_OUTDIR := mobile/src/main/assets/webui +WEBUI_SOURCES := $(shell find $(RS_SRCDIR) -type f -name *.rs) + +aw-webui: $(WEBUI_OUTDIR) + +.PHONY: $(WEBUI_OUTDIR) +$(WEBUI_OUTDIR): $(WEBUI_SRCDIR)/dist mkdir -p mobile/src/main/assets/webui - cp -r aw-server-rust/aw-webui/dist/* mobile/src/main/assets/webui + cp -r $(WEBUI_SRCDIR)/dist/* mobile/src/main/assets/webui + +.PHONY: $(WEBUI_SRCDIR)/dist +$(WEBUI_SRCDIR)/dist: + # Ideally this sub-Makefile should not rebuild unless files have changed + make --directory=aw-server-rust/aw-webui build + +clean: + rm -rf mobile/src/main/assets/webui + rm -rf mobile/src/main/jniLibs diff --git a/aw-server-rust b/aw-server-rust index 2b55a975..00ce14b7 160000 --- a/aw-server-rust +++ b/aw-server-rust @@ -1 +1 @@ -Subproject commit 2b55a97549429c4b570b28bd8d05396a22d8046e +Subproject commit 00ce14b79a1d4c93dc62ecbce6f00fd43505bbc3 diff --git a/build.gradle b/build.gradle index a3176b9e..cc4a60fb 100644 --- a/build.gradle +++ b/build.gradle @@ -5,10 +5,14 @@ buildscript { repositories { google() jcenter() + maven { + url "https://plugins.gradle.org/m2/" + } } dependencies { classpath 'com.android.tools.build:gradle:3.2.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'gradle.plugin.org.mozilla.rust-android-gradle:plugin:0.8.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/fastlane/Fastfile b/fastlane/Fastfile index e35f8f9e..719465b3 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -21,10 +21,10 @@ platform :android do gradle(task: "test") end - desc "Submit a new Beta Build to Crashlytics Beta" + desc "Build a new Beta build" lane :beta do gradle(task: "clean assembleRelease") - crashlytics + #crashlytics # sh "your_script.sh" # You can also use other beta testing services here diff --git a/fastlane/README.md b/fastlane/README.md index 0c131abe..013107ea 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -25,12 +25,12 @@ Runs all the tests ``` fastlane android beta ``` -Submit a new Beta Build to Crashlytics Beta +Build a new Beta build ### android update_version ``` fastlane android update_version ``` - +Update versionCode to be one higher than the latest on Play Store ### android deploy ``` fastlane android deploy diff --git a/gradle.properties b/gradle.properties index 85be9ead..3d8ce0ca 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,3 +13,5 @@ org.gradle.jvmargs=-Xmx1536m # org.gradle.parallel=true # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official +android.useAndroidX=true +android.enableJetifier=true diff --git a/mobile/build.gradle b/mobile/build.gradle index 211ffbcd..b6fa2c84 100644 --- a/mobile/build.gradle +++ b/mobile/build.gradle @@ -5,15 +5,19 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { applicationId "net.activitywatch.android" - minSdkVersion 24 - targetSdkVersion 28 - // versionCode and versionName are updated automatically in deploy script - versionCode 14 - versionName "0.4-dev" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + minSdkVersion 23 + targetSdkVersion 29 + + // Set in CI on tagged commit + versionName "0.9-dev" + + // Set in CI by `bundle exec fastlane update_version` + versionCode 22 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // WARNING: Never commit this uncommented! //packagingOptions { @@ -29,13 +33,18 @@ android { applicationIdSuffix ".debug" } } + compileOptions { + sourceCompatibility = "1.8" + targetCompatibility = 1.8 + } + buildToolsVersion = '29.0.3' // Never got this to work... - if (project.hasProperty("doNotStrip")) { - packagingOptions { - doNotStrip '**/*.so' - } - } + //if (project.hasProperty("doNotStrip")) { + // packagingOptions { + // doNotStrip '**/*.so' + // } + //} // Creates a resource versionName with the full version // https://stackoverflow.com/a/36468650/965332 @@ -47,15 +56,41 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'com.android.support:appcompat-v7:28.0.0' - implementation 'com.android.support:support-v4:28.0.0' - implementation 'com.android.support:design:28.0.0' - implementation 'com.android.support:cardview-v7:28.0.0' - implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'com.google.android.material:material:1.1.0' + implementation 'androidx.cardview:cardview:1.0.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'com.jakewharton.threetenabp:threetenabp:1.1.1' - implementation 'com.android.support:recyclerview-v7:28.0.0' - implementation 'android.arch.lifecycle:extensions:1.1.1' + implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' +} + +// Can be used to build with: ./gradlew cargoBuild +// NOTE: Doesn't work, chokes on building openssl-sys +apply plugin: 'org.mozilla.rust-android-gradle.rust-android' + +cargo { + module = "../aw-server-rust" // Or whatever directory contains your Cargo.toml + libname = "aw_server" // Or whatever matches Cargo.toml's [package] name. + targets = ["arm", "arm64", "x86", "x86_64"] // See bellow for a longer list of options + profile = 'release' // Selects the Cargo release profile (defaults to 'debug') +} + +tasks.whenTaskAdded { task -> + // TODO: Build aw-server lib here instead of in Makefile? + // Doesn't work, chokes on building openssl-sys + //if ((task.name == 'javaPreCompileDebug' || task.name == 'javaPreCompileRelease')) { + // task.dependsOn 'cargoBuild' + //} + + // TODO: Build aw-webui here? + //if (task.name.contains("assembleRelease")) { + // task.getDependsOn().add({ + // // add your logic here + // }) + //} } diff --git a/mobile/src/androidTest/java/net/activitywatch/android/ExampleInstrumentedTest.kt b/mobile/src/androidTest/java/net/activitywatch/android/ExampleInstrumentedTest.kt index 7f7d15b7..0dc8bb4c 100644 --- a/mobile/src/androidTest/java/net/activitywatch/android/ExampleInstrumentedTest.kt +++ b/mobile/src/androidTest/java/net/activitywatch/android/ExampleInstrumentedTest.kt @@ -1,7 +1,7 @@ package net.activitywatch.android -import android.support.test.InstrumentationRegistry -import android.support.test.runner.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.Test import org.junit.Before diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index 80757c33..ed602ec6 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -3,30 +3,44 @@ xmlns:tools="http://schemas.android.com/tools" package="net.activitywatch.android"> + + + + + android:networkSecurityConfig="@xml/network_security_config"> + + - + + + + + $destPath") - try { - val asset = context.assets.open(sourcePath) - val size = asset.available() - val buffer = ByteArray(size) - asset.read(buffer) - asset.close() - - val fos = FileOutputStream(f) - fos.write(buffer) - fos.close() - } catch (e: Exception) { - throw RuntimeException(e) + //Log.w(TAG, "path: $path") + //Log.w(TAG, "files: ${filenames!!.joinToString(", ")}") + if (filenames != null) { + for(fn in filenames) { + val sourcePath = path + File.separator + fn + val destPath = context.cacheDir.path + File.separator + path + File.separator + fn + val f = File(destPath) + + // If directory, recurse + // TODO: Fix this ugly folder-guess hack + if(!f.path.substringAfterLast("/").contains(".")) { + //Log.w(TAG, "WAS DIR") + extractAssets(sourcePath, context, overwrite) + continue + } + + + // Create parent directory if missing + val dir = f.parentFile + if(!dir.exists()) { + dir.mkdirs() + } + + // Write file if it doesn't exist, or if overwrite is true + // TODO: Fix the ugly folder-detection hack + if ((!f.exists() || overwrite) && f.path.substringAfterLast("/").contains(".")) { + Log.w(TAG, "$sourcePath -> $destPath") + try { + val asset = context.assets.open(sourcePath) + val size = asset.available() + val buffer = ByteArray(size) + asset.read(buffer) + asset.close() + + val fos = FileOutputStream(f) + fos.write(buffer) + fos.close() + } catch (e: Exception) { + throw RuntimeException(e) + } + } else { + Log.w(TAG, "Skipped: $sourcePath -> $destPath") } } } diff --git a/mobile/src/main/java/net/activitywatch/android/MainActivity.kt b/mobile/src/main/java/net/activitywatch/android/MainActivity.kt index b5c3dffb..63ac0d1f 100644 --- a/mobile/src/main/java/net/activitywatch/android/MainActivity.kt +++ b/mobile/src/main/java/net/activitywatch/android/MainActivity.kt @@ -1,18 +1,17 @@ package net.activitywatch.android -import android.app.usage.UsageStats import android.net.Uri import android.os.Bundle -import android.support.design.widget.Snackbar -import android.support.design.widget.NavigationView -import android.support.v4.view.GravityCompat -import android.support.v7.app.ActionBarDrawerToggle -import android.support.v7.app.AppCompatActivity +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.navigation.NavigationView +import androidx.core.view.GravityCompat +import androidx.appcompat.app.ActionBarDrawerToggle +import androidx.appcompat.app.AppCompatActivity import android.view.Menu import android.view.MenuItem import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.app_bar_main.* -import android.support.v4.app.Fragment +import androidx.fragment.app.Fragment import android.util.Log import net.activitywatch.android.fragments.Bucket import net.activitywatch.android.fragments.BucketListFragment @@ -20,11 +19,12 @@ import net.activitywatch.android.fragments.TestFragment import net.activitywatch.android.fragments.WebUIFragment import net.activitywatch.android.watcher.UsageStatsWatcher +private const val TAG = "MainActivity" + + class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener, BucketListFragment.OnListFragmentInteractionListener, WebUIFragment.OnFragmentInteractionListener { - private val TAG = "MainActivity" - val version: String get() { return packageManager.getPackageInfo(packageName, 0).versionName @@ -98,29 +98,36 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. - when (item.itemId) { + return when (item.itemId) { R.id.action_settings -> { Snackbar.make(coordinator_layout, "The settings button was clicked, but it's not yet implemented!", Snackbar.LENGTH_LONG) .setAction("Action", null).show() - return true + true } - else -> return super.onOptionsItemSelected(item) + else -> super.onOptionsItemSelected(item) } } override fun onNavigationItemSelected(item: MenuItem): Boolean { var fragmentClass: Class? = null + var url: String? = null + val base = "http://127.0.0.1:5600" // Handle navigation view item clicks here. when (item.itemId) { R.id.nav_dashboard -> { fragmentClass = TestFragment::class.java } - R.id.nav_webui -> { + R.id.nav_activity -> { + fragmentClass = WebUIFragment::class.java + url = "$base/#/activity/unknown/" + } + R.id.nav_buckets -> { fragmentClass = WebUIFragment::class.java + url = "$base/#/buckets/" } R.id.nav_settings -> { - Snackbar.make(coordinator_layout, "The settings button was clicked, but it's not yet implemented!", Snackbar.LENGTH_LONG) - .setAction("Action", null).show() + fragmentClass = WebUIFragment::class.java + url = "$base/#/settings/" } R.id.nav_share -> { Snackbar.make(coordinator_layout, "The share button was clicked, but it's not yet implemented!", Snackbar.LENGTH_LONG) @@ -133,7 +140,11 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte } val fragment: Fragment? = try { - fragmentClass?.newInstance() + if (fragmentClass === WebUIFragment::class.java && url != null) { + WebUIFragment.newInstance(url) + } else { + fragmentClass?.newInstance() + } } catch (e: Exception) { e.printStackTrace() null diff --git a/mobile/src/main/java/net/activitywatch/android/RustInterface.kt b/mobile/src/main/java/net/activitywatch/android/RustInterface.kt index 3c75e429..dee5de5e 100644 --- a/mobile/src/main/java/net/activitywatch/android/RustInterface.kt +++ b/mobile/src/main/java/net/activitywatch/android/RustInterface.kt @@ -59,15 +59,10 @@ class RustInterface constructor(context: Context? = null) { // TODO: This probably shouldn't be an AsyncTask private inner class ServerTask(val context: Context) : AsyncTask() { override fun doInBackground(vararg inputs: String) { - val assetDir = context.cacheDir.path + File.separator + "webui" - - // TODO: Extract assets recursively instead AssetExtractor.extractAssets("webui", context) - AssetExtractor.extractAssets("webui/static", context) - AssetExtractor.extractAssets("webui/static/js", context) - AssetExtractor.extractAssets("webui/static/fonts", context) Log.w(TAG, "Starting server...") + val assetDir = context.cacheDir.path + File.separator + "webui" startServer(assetDir) } } diff --git a/mobile/src/main/java/net/activitywatch/android/fragments/BucketFragment.kt b/mobile/src/main/java/net/activitywatch/android/fragments/BucketFragment.kt index ce3e6b0c..7d224a67 100644 --- a/mobile/src/main/java/net/activitywatch/android/fragments/BucketFragment.kt +++ b/mobile/src/main/java/net/activitywatch/android/fragments/BucketFragment.kt @@ -1,8 +1,8 @@ package net.activitywatch.android.fragments -import android.arch.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProviders import android.os.Bundle -import android.support.v4.app.Fragment +import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup diff --git a/mobile/src/main/java/net/activitywatch/android/fragments/BucketListFragment.kt b/mobile/src/main/java/net/activitywatch/android/fragments/BucketListFragment.kt index f77fea8a..9fad7aa6 100644 --- a/mobile/src/main/java/net/activitywatch/android/fragments/BucketListFragment.kt +++ b/mobile/src/main/java/net/activitywatch/android/fragments/BucketListFragment.kt @@ -2,10 +2,10 @@ package net.activitywatch.android.fragments import android.content.Context import android.os.Bundle -import android.support.v4.app.Fragment -import android.support.v7.widget.GridLayoutManager -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.RecyclerView +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import android.util.Log import android.view.LayoutInflater import android.view.View diff --git a/mobile/src/main/java/net/activitywatch/android/fragments/MyBucketRecyclerViewAdapter.kt b/mobile/src/main/java/net/activitywatch/android/fragments/MyBucketRecyclerViewAdapter.kt index 31bee135..b04d0c20 100644 --- a/mobile/src/main/java/net/activitywatch/android/fragments/MyBucketRecyclerViewAdapter.kt +++ b/mobile/src/main/java/net/activitywatch/android/fragments/MyBucketRecyclerViewAdapter.kt @@ -1,6 +1,6 @@ package net.activitywatch.android.fragments -import android.support.v7.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup diff --git a/mobile/src/main/java/net/activitywatch/android/fragments/TestFragment.kt b/mobile/src/main/java/net/activitywatch/android/fragments/TestFragment.kt index 86de2746..1c90c4a3 100644 --- a/mobile/src/main/java/net/activitywatch/android/fragments/TestFragment.kt +++ b/mobile/src/main/java/net/activitywatch/android/fragments/TestFragment.kt @@ -1,9 +1,9 @@ package net.activitywatch.android.fragments -import android.arch.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProviders import android.os.Bundle -import android.support.design.widget.Snackbar -import android.support.v4.app.Fragment +import com.google.android.material.snackbar.Snackbar +import androidx.fragment.app.Fragment import android.text.method.LinkMovementMethod import android.util.Log import android.view.LayoutInflater diff --git a/mobile/src/main/java/net/activitywatch/android/fragments/WebUIFragment.kt b/mobile/src/main/java/net/activitywatch/android/fragments/WebUIFragment.kt index 50ae184b..d31816fe 100644 --- a/mobile/src/main/java/net/activitywatch/android/fragments/WebUIFragment.kt +++ b/mobile/src/main/java/net/activitywatch/android/fragments/WebUIFragment.kt @@ -5,7 +5,7 @@ import android.content.Intent import android.content.pm.ApplicationInfo import android.net.Uri import android.os.Bundle -import android.support.v4.app.Fragment +import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -25,8 +25,7 @@ import android.webkit.DownloadListener // TODO: Rename parameter arguments, choose names that match // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER -private const val ARG_PARAM1 = "param1" -private const val ARG_PARAM2 = "param2" +private const val ARG_URL = "url" /** * A simple [Fragment] subclass. @@ -39,15 +38,13 @@ private const val ARG_PARAM2 = "param2" */ class WebUIFragment : Fragment() { // TODO: Rename and change types of parameters - private var param1: String? = null - private var param2: String? = null + private var url: String? = null private var listener: OnFragmentInteractionListener? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { - param1 = it.getString(ARG_PARAM1) - param2 = it.getString(ARG_PARAM2) + url = it.getString(ARG_URL) } } @@ -74,7 +71,10 @@ class WebUIFragment : Fragment() { myWebView.settings.javaScriptEnabled = true myWebView.settings.domStorageEnabled = true - myWebView.loadUrl("http://127.0.0.1:5600") + //myWebView.loadUrl("http://127.0.0.1:5600") + arguments?.let { + myWebView.loadUrl(it.getString(ARG_URL)) + } return view } @@ -125,11 +125,10 @@ class WebUIFragment : Fragment() { */ // TODO: Rename and change types and number of parameters @JvmStatic - fun newInstance(param1: String, param2: String) = + fun newInstance(url: String) = WebUIFragment().apply { arguments = Bundle().apply { - putString(ARG_PARAM1, param1) - putString(ARG_PARAM2, param2) + putString(ARG_URL, url) } } } diff --git a/mobile/src/main/java/net/activitywatch/android/models/BucketViewModel.kt b/mobile/src/main/java/net/activitywatch/android/models/BucketViewModel.kt index 6a1c5ab9..957c67eb 100644 --- a/mobile/src/main/java/net/activitywatch/android/models/BucketViewModel.kt +++ b/mobile/src/main/java/net/activitywatch/android/models/BucketViewModel.kt @@ -1,6 +1,6 @@ package net.activitywatch.android.models -import android.arch.lifecycle.ViewModel; +import androidx.lifecycle.ViewModel; import org.json.JSONObject class BucketViewModel(val json: JSONObject) : ViewModel() { diff --git a/mobile/src/main/java/net/activitywatch/android/models/TestViewModel.kt b/mobile/src/main/java/net/activitywatch/android/models/TestViewModel.kt index 2e75cdf3..84279ef3 100644 --- a/mobile/src/main/java/net/activitywatch/android/models/TestViewModel.kt +++ b/mobile/src/main/java/net/activitywatch/android/models/TestViewModel.kt @@ -1,6 +1,6 @@ package net.activitywatch.android.models -import android.arch.lifecycle.ViewModel; +import androidx.lifecycle.ViewModel; class TestViewModel : ViewModel() { // TODO: Implement the ViewModel diff --git a/mobile/src/main/res/layout/activity_main.xml b/mobile/src/main/res/layout/activity_main.xml index e6f22d06..4695b186 100644 --- a/mobile/src/main/res/layout/activity_main.xml +++ b/mobile/src/main/res/layout/activity_main.xml @@ -1,5 +1,5 @@ - - - - + - + diff --git a/mobile/src/main/res/layout/app_bar_main.xml b/mobile/src/main/res/layout/app_bar_main.xml index 978fa39e..4c84fb92 100644 --- a/mobile/src/main/res/layout/app_bar_main.xml +++ b/mobile/src/main/res/layout/app_bar_main.xml @@ -1,5 +1,5 @@ - - - - + - \ No newline at end of file + \ No newline at end of file diff --git a/mobile/src/main/res/layout/fragment_bucket.xml b/mobile/src/main/res/layout/fragment_bucket.xml index 91c851c1..1f246d4a 100644 --- a/mobile/src/main/res/layout/fragment_bucket.xml +++ b/mobile/src/main/res/layout/fragment_bucket.xml @@ -1,5 +1,5 @@ - - + diff --git a/mobile/src/main/res/layout/fragment_bucket_list.xml b/mobile/src/main/res/layout/fragment_bucket_list.xml index ebc718df..2518623c 100644 --- a/mobile/src/main/res/layout/fragment_bucket_list.xml +++ b/mobile/src/main/res/layout/fragment_bucket_list.xml @@ -1,5 +1,5 @@ - - + diff --git a/mobile/src/main/res/menu/activity_main_drawer.xml b/mobile/src/main/res/menu/activity_main_drawer.xml index 4e1d66bf..c5db4b3e 100644 --- a/mobile/src/main/res/menu/activity_main_drawer.xml +++ b/mobile/src/main/res/menu/activity_main_drawer.xml @@ -9,23 +9,22 @@ android:icon="@drawable/ic_menu_slideshow" android:title="Home" /> + android:title="Activity" /> + - + - /dev/null 2>&1 && pwd )" project_path="$(readlink -f "$script_dir/..")" -if [ -z "$ANDROID_HOME" ]; then - # TODO: Remove this, bad practice - export ANDROID_HOME=/home/$USER/Android/Sdk - export ANDROID_NDK_HOME=$ANDROID_HOME/ndk-bundle +if [ -z "$ANDROID_NDK_HOME" ]; then + echo "ANDROID_NDK_HOME not set, exiting." + exit 1 fi -# curl https://sh.rustup.rs -sSf | sh - -# TODO: Remove this, bad practice -$ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --api 28 --arch arm64 --install-dir $ANDROID_NDK_HOME/arm64 -$ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --api 28 --arch arm --install-dir $ANDROID_NDK_HOME/arm -$ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --api 28 --arch x86 --install-dir $ANDROID_NDK_HOME/x86 -$ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --api 28 --arch x86_64 --install-dir $ANDROID_NDK_HOME/x86_64 - -echo " -[target.aarch64-linux-android] -ar = '$ANDROID_NDK_HOME/arm64/bin/aarch64-linux-android-ar' -linker = '$ANDROID_NDK_HOME/arm64/bin/aarch64-linux-android-clang' - -[target.armv7-linux-androideabi] -ar = '$ANDROID_NDK_HOME/arm/bin/arm-linux-androideabi-ar' -linker = '$ANDROID_NDK_HOME/arm/bin/arm-linux-androideabi-clang' - -[target.i686-linux-android] -ar = '$ANDROID_NDK_HOME/x86/bin/i686-linux-android-ar' -linker = '$ANDROID_NDK_HOME/x86/bin/i686-linux-android-clang' - -[target.x86_64-linux-android] -ar = '$ANDROID_NDK_HOME/x86_64/bin/x86_64-linux-android-ar' -linker = '$ANDROID_NDK_HOME/x86_64/bin/x86_64-linux-android-clang' -" > aw-server-rust/.cargo/config - -rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android +# Runs non-Android specific setup tasks +# - Creates some symlinks in the ANDROID_NDK_HOME toolchains to work around ring weirdness +# - Creates the cargo config +# - Retrieves target dependencies with rustup +$project_path/aw-server-rust/install-ndk.sh +# Create destination folders for built libraries mkdir -p $project_path/mobile/src/main/jniLibs/x86 mkdir -p $project_path/mobile/src/main/jniLibs/x86_64 mkdir -p $project_path/mobile/src/main/jniLibs/arm64 @@ -49,5 +27,5 @@ mkdir -p $project_path/mobile/src/main/jniLibs/armeabi # Some more steps after this is done: # - Build aw-server-rust using its compile-android.sh script -# - Copy/link the built libraries into the mobile/src/main/jniLibs folder +# - Copy/link the built libraries into the mobile/src/main/jniLibs folders # - Build and test the app! diff --git a/settings.gradle b/settings.gradle index b42e48ff..6070d9b3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':mobile', ':tv' +include ':mobile' diff --git a/tv/.gitignore b/tv/.gitignore deleted file mode 100644 index 796b96d1..00000000 --- a/tv/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/tv/build.gradle b/tv/build.gradle deleted file mode 100644 index 2b005092..00000000 --- a/tv/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -apply plugin: 'com.android.application' - -apply plugin: 'kotlin-android' - -apply plugin: 'kotlin-android-extensions' - -android { - compileSdkVersion 27 - defaultConfig { - applicationId "net.activitywatch.android" - minSdkVersion 26 - targetSdkVersion 27 - versionCode 1 - versionName "1.0" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'com.android.support:leanback-v17:27.1.1' - implementation 'com.android.support:appcompat-v7:27.1.1' - implementation 'com.github.bumptech.glide:glide:3.8.0' -} diff --git a/tv/proguard-rules.pro b/tv/proguard-rules.pro deleted file mode 100644 index f1b42451..00000000 --- a/tv/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/tv/src/main/AndroidManifest.xml b/tv/src/main/AndroidManifest.xml deleted file mode 100644 index ede6c316..00000000 --- a/tv/src/main/AndroidManifest.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/tv/src/main/java/net/activitywatch/android/BrowseErrorActivity.kt b/tv/src/main/java/net/activitywatch/android/BrowseErrorActivity.kt deleted file mode 100644 index abaacb75..00000000 --- a/tv/src/main/java/net/activitywatch/android/BrowseErrorActivity.kt +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package net.activitywatch.android - -import android.app.Activity -import android.app.Fragment -import android.os.Bundle -import android.os.Handler -import android.view.Gravity -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.FrameLayout -import android.widget.ProgressBar - -/** - * BrowseErrorActivity shows how to use ErrorFragment. - */ -class BrowseErrorActivity : Activity() { - - private lateinit var mErrorFragment: ErrorFragment - private lateinit var mSpinnerFragment: SpinnerFragment - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - - testError() - } - - private fun testError() { - mErrorFragment = ErrorFragment() - fragmentManager - .beginTransaction() - .add(R.id.main_browse_fragment, mErrorFragment) - .commit() - - mSpinnerFragment = SpinnerFragment() - fragmentManager - .beginTransaction() - .add(R.id.main_browse_fragment, mSpinnerFragment) - .commit() - - val handler = Handler() - handler.postDelayed({ - fragmentManager - .beginTransaction() - .remove(mSpinnerFragment) - .commit() - mErrorFragment.setErrorContent() - }, TIMER_DELAY) - } - - class SpinnerFragment : Fragment() { - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - val progressBar = ProgressBar(container?.context) - if (container is FrameLayout) { - val layoutParams = FrameLayout.LayoutParams(SPINNER_WIDTH, SPINNER_HEIGHT, Gravity.CENTER) - progressBar.layoutParams = layoutParams - } - return progressBar - } - } - - companion object { - private val TIMER_DELAY = 3000L - private val SPINNER_WIDTH = 100 - private val SPINNER_HEIGHT = 100 - } -} \ No newline at end of file diff --git a/tv/src/main/java/net/activitywatch/android/CardPresenter.kt b/tv/src/main/java/net/activitywatch/android/CardPresenter.kt deleted file mode 100644 index 3caedad2..00000000 --- a/tv/src/main/java/net/activitywatch/android/CardPresenter.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package net.activitywatch.android - -import android.graphics.drawable.Drawable -import android.support.v17.leanback.widget.ImageCardView -import android.support.v17.leanback.widget.Presenter -import android.support.v4.content.ContextCompat -import android.util.Log -import android.view.ViewGroup - -import com.bumptech.glide.Glide -import kotlin.properties.Delegates - -/** - * A CardPresenter is used to generate Views and bind Objects to them on demand. - * It contains an ImageCardView. - */ -class CardPresenter : Presenter() { - private var mDefaultCardImage: Drawable? = null - private var sSelectedBackgroundColor: Int by Delegates.notNull() - private var sDefaultBackgroundColor: Int by Delegates.notNull() - - override fun onCreateViewHolder(parent: ViewGroup): Presenter.ViewHolder { - Log.d(TAG, "onCreateViewHolder") - - sDefaultBackgroundColor = ContextCompat.getColor(parent.context, R.color.default_background) - sSelectedBackgroundColor = ContextCompat.getColor(parent.context, R.color.selected_background) - mDefaultCardImage = ContextCompat.getDrawable(parent.context, R.drawable.movie) - - val cardView = object : ImageCardView(parent.context) { - override fun setSelected(selected: Boolean) { - updateCardBackgroundColor(this, selected) - super.setSelected(selected) - } - } - - cardView.isFocusable = true - cardView.isFocusableInTouchMode = true - updateCardBackgroundColor(cardView, false) - return Presenter.ViewHolder(cardView) - } - - override fun onBindViewHolder(viewHolder: Presenter.ViewHolder, item: Any) { - val movie = item as Movie - val cardView = viewHolder.view as ImageCardView - - Log.d(TAG, "onBindViewHolder") - if (movie.cardImageUrl != null) { - cardView.titleText = movie.title - cardView.contentText = movie.studio - cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT) - Glide.with(viewHolder.view.context) - .load(movie.cardImageUrl) - .centerCrop() - .error(mDefaultCardImage) - .into(cardView.mainImageView) - } - } - - override fun onUnbindViewHolder(viewHolder: Presenter.ViewHolder) { - Log.d(TAG, "onUnbindViewHolder") - val cardView = viewHolder.view as ImageCardView - // Remove references to images so that the garbage collector can free up memory - cardView.badgeImage = null - cardView.mainImage = null - } - - private fun updateCardBackgroundColor(view: ImageCardView, selected: Boolean) { - val color = if (selected) sSelectedBackgroundColor else sDefaultBackgroundColor - // Both background colors should be set because the view's background is temporarily visible - // during animations. - view.setBackgroundColor(color) - view.setInfoAreaBackgroundColor(color) - } - - companion object { - private val TAG = "CardPresenter" - - private val CARD_WIDTH = 313 - private val CARD_HEIGHT = 176 - } -} diff --git a/tv/src/main/java/net/activitywatch/android/DetailsActivity.kt b/tv/src/main/java/net/activitywatch/android/DetailsActivity.kt deleted file mode 100644 index 3a9fc4aa..00000000 --- a/tv/src/main/java/net/activitywatch/android/DetailsActivity.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package net.activitywatch.android - -import android.app.Activity -import android.os.Bundle - -/** - * Details activity class that loads [VideoDetailsFragment] class. - */ -class DetailsActivity : Activity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_details) - } - - companion object { - const val SHARED_ELEMENT_NAME = "hero" - const val MOVIE = "Movie" - } -} diff --git a/tv/src/main/java/net/activitywatch/android/DetailsDescriptionPresenter.kt b/tv/src/main/java/net/activitywatch/android/DetailsDescriptionPresenter.kt deleted file mode 100644 index 452b2116..00000000 --- a/tv/src/main/java/net/activitywatch/android/DetailsDescriptionPresenter.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package net.activitywatch.android - -import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter - -class DetailsDescriptionPresenter : AbstractDetailsDescriptionPresenter() { - - override fun onBindDescription( - viewHolder: AbstractDetailsDescriptionPresenter.ViewHolder, - item: Any - ) { - val movie = item as Movie - - viewHolder.title.text = movie.title - viewHolder.subtitle.text = movie.studio - viewHolder.body.text = movie.description - } -} \ No newline at end of file diff --git a/tv/src/main/java/net/activitywatch/android/ErrorFragment.kt b/tv/src/main/java/net/activitywatch/android/ErrorFragment.kt deleted file mode 100644 index 19b17498..00000000 --- a/tv/src/main/java/net/activitywatch/android/ErrorFragment.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package net.activitywatch.android - -import android.os.Bundle -import android.support.v4.content.ContextCompat -import android.view.View - -/** - * This class demonstrates how to extend [android.support.v17.leanback.app.ErrorFragment]. - */ -class ErrorFragment : android.support.v17.leanback.app.ErrorFragment() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - title = resources.getString(R.string.app_name) - } - - internal fun setErrorContent() { - imageDrawable = ContextCompat.getDrawable(context, R.drawable.lb_ic_sad_cloud) - message = resources.getString(R.string.error_fragment_message) - setDefaultBackground(TRANSLUCENT) - - buttonText = resources.getString(R.string.dismiss_error) - buttonClickListener = View.OnClickListener { - fragmentManager.beginTransaction().remove(this@ErrorFragment).commit() - } - } - - companion object { - private val TRANSLUCENT = true - } -} \ No newline at end of file diff --git a/tv/src/main/java/net/activitywatch/android/MainActivity.kt b/tv/src/main/java/net/activitywatch/android/MainActivity.kt deleted file mode 100644 index 21a96854..00000000 --- a/tv/src/main/java/net/activitywatch/android/MainActivity.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package net.activitywatch.android - -import android.app.Activity -import android.os.Bundle - -/** - * Loads [MainFragment]. - */ -class MainActivity : Activity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - } -} diff --git a/tv/src/main/java/net/activitywatch/android/MainFragment.kt b/tv/src/main/java/net/activitywatch/android/MainFragment.kt deleted file mode 100644 index a8a875c1..00000000 --- a/tv/src/main/java/net/activitywatch/android/MainFragment.kt +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package net.activitywatch.android - -import java.util.Collections -import java.util.Timer -import java.util.TimerTask - -import android.content.Intent -import android.graphics.Color -import android.graphics.drawable.Drawable -import android.os.Bundle -import android.os.Handler -import android.support.v17.leanback.app.BackgroundManager -import android.support.v17.leanback.app.BrowseFragment -import android.support.v17.leanback.widget.ArrayObjectAdapter -import android.support.v17.leanback.widget.HeaderItem -import android.support.v17.leanback.widget.ImageCardView -import android.support.v17.leanback.widget.ListRow -import android.support.v17.leanback.widget.ListRowPresenter -import android.support.v17.leanback.widget.OnItemViewClickedListener -import android.support.v17.leanback.widget.OnItemViewSelectedListener -import android.support.v17.leanback.widget.Presenter -import android.support.v17.leanback.widget.Row -import android.support.v17.leanback.widget.RowPresenter -import android.support.v4.app.ActivityOptionsCompat -import android.support.v4.content.ContextCompat -import android.util.DisplayMetrics -import android.util.Log -import android.view.Gravity -import android.view.ViewGroup -import android.widget.TextView -import android.widget.Toast - -import com.bumptech.glide.Glide -import com.bumptech.glide.load.resource.drawable.GlideDrawable -import com.bumptech.glide.request.animation.GlideAnimation -import com.bumptech.glide.request.target.SimpleTarget - -/** - * Loads a grid of cards with movies to browse. - */ -class MainFragment : BrowseFragment() { - - private val mHandler = Handler() - private lateinit var mBackgroundManager: BackgroundManager - private var mDefaultBackground: Drawable? = null - private lateinit var mMetrics: DisplayMetrics - private var mBackgroundTimer: Timer? = null - private var mBackgroundUri: String? = null - - override fun onActivityCreated(savedInstanceState: Bundle?) { - Log.i(TAG, "onCreate") - super.onActivityCreated(savedInstanceState) - - prepareBackgroundManager() - - setupUIElements() - - loadRows() - - setupEventListeners() - } - - override fun onDestroy() { - super.onDestroy() - Log.d(TAG, "onDestroy: " + mBackgroundTimer?.toString()) - mBackgroundTimer?.cancel() - } - - private fun prepareBackgroundManager() { - - mBackgroundManager = BackgroundManager.getInstance(activity) - mBackgroundManager.attach(activity.window) - mDefaultBackground = ContextCompat.getDrawable(context, R.drawable.default_background) - mMetrics = DisplayMetrics() - activity.windowManager.defaultDisplay.getMetrics(mMetrics) - } - - private fun setupUIElements() { - title = getString(R.string.browse_title) - // over title - headersState = BrowseFragment.HEADERS_ENABLED - isHeadersTransitionOnBackEnabled = true - - // set fastLane (or headers) background color - brandColor = ContextCompat.getColor(context, R.color.fastlane_background) - // set search icon color - searchAffordanceColor = ContextCompat.getColor(context, R.color.search_opaque) - } - - private fun loadRows() { - val list = MovieList.list - - val rowsAdapter = ArrayObjectAdapter(ListRowPresenter()) - val cardPresenter = CardPresenter() - - for (i in 0 until NUM_ROWS) { - if (i != 0) { - Collections.shuffle(list) - } - val listRowAdapter = ArrayObjectAdapter(cardPresenter) - for (j in 0 until NUM_COLS) { - listRowAdapter.add(list[j % 5]) - } - val header = HeaderItem(i.toLong(), MovieList.MOVIE_CATEGORY[i]) - rowsAdapter.add(ListRow(header, listRowAdapter)) - } - - val gridHeader = HeaderItem(NUM_ROWS.toLong(), "PREFERENCES") - - val mGridPresenter = GridItemPresenter() - val gridRowAdapter = ArrayObjectAdapter(mGridPresenter) - gridRowAdapter.add(resources.getString(R.string.grid_view)) - gridRowAdapter.add(getString(R.string.error_fragment)) - gridRowAdapter.add(resources.getString(R.string.personal_settings)) - rowsAdapter.add(ListRow(gridHeader, gridRowAdapter)) - - adapter = rowsAdapter - } - - private fun setupEventListeners() { - setOnSearchClickedListener { - Toast.makeText(context, "Implement your own in-app search", Toast.LENGTH_LONG) - .show() - } - - onItemViewClickedListener = ItemViewClickedListener() - onItemViewSelectedListener = ItemViewSelectedListener() - } - - private inner class ItemViewClickedListener : OnItemViewClickedListener { - override fun onItemClicked( - itemViewHolder: Presenter.ViewHolder, - item: Any, - rowViewHolder: RowPresenter.ViewHolder, - row: Row - ) { - - if (item is Movie) { - Log.d(TAG, "Item: " + item.toString()) - val intent = Intent(context, DetailsActivity::class.java) - intent.putExtra(DetailsActivity.MOVIE, item) - - val bundle = ActivityOptionsCompat.makeSceneTransitionAnimation( - activity, - (itemViewHolder.view as ImageCardView).mainImageView, - DetailsActivity.SHARED_ELEMENT_NAME - ) - .toBundle() - activity.startActivity(intent, bundle) - } else if (item is String) { - if (item.contains(getString(R.string.error_fragment))) { - val intent = Intent(context, BrowseErrorActivity::class.java) - startActivity(intent) - } else { - Toast.makeText(context, item, Toast.LENGTH_SHORT).show() - } - } - } - } - - private inner class ItemViewSelectedListener : OnItemViewSelectedListener { - override fun onItemSelected( - itemViewHolder: Presenter.ViewHolder?, item: Any?, - rowViewHolder: RowPresenter.ViewHolder, row: Row - ) { - if (item is Movie) { - mBackgroundUri = item.backgroundImageUrl - startBackgroundTimer() - } - } - } - - private fun updateBackground(uri: String?) { - val width = mMetrics.widthPixels - val height = mMetrics.heightPixels - Glide.with(context) - .load(uri) - .centerCrop() - .error(mDefaultBackground) - .into>( - object : SimpleTarget(width, height) { - override fun onResourceReady( - resource: GlideDrawable, - glideAnimation: GlideAnimation - ) { - mBackgroundManager.drawable = resource - } - }) - mBackgroundTimer?.cancel() - } - - private fun startBackgroundTimer() { - mBackgroundTimer?.cancel() - mBackgroundTimer = Timer() - mBackgroundTimer?.schedule(UpdateBackgroundTask(), BACKGROUND_UPDATE_DELAY.toLong()) - } - - private inner class UpdateBackgroundTask : TimerTask() { - - override fun run() { - mHandler.post { updateBackground(mBackgroundUri) } - } - } - - private inner class GridItemPresenter : Presenter() { - override fun onCreateViewHolder(parent: ViewGroup): Presenter.ViewHolder { - val view = TextView(parent.context) - view.layoutParams = ViewGroup.LayoutParams(GRID_ITEM_WIDTH, GRID_ITEM_HEIGHT) - view.isFocusable = true - view.isFocusableInTouchMode = true - view.setBackgroundColor(ContextCompat.getColor(context, R.color.default_background)) - view.setTextColor(Color.WHITE) - view.gravity = Gravity.CENTER - return Presenter.ViewHolder(view) - } - - override fun onBindViewHolder(viewHolder: Presenter.ViewHolder, item: Any) { - (viewHolder.view as TextView).text = item as String - } - - override fun onUnbindViewHolder(viewHolder: Presenter.ViewHolder) {} - } - - companion object { - private val TAG = "MainFragment" - - private val BACKGROUND_UPDATE_DELAY = 300 - private val GRID_ITEM_WIDTH = 200 - private val GRID_ITEM_HEIGHT = 200 - private val NUM_ROWS = 6 - private val NUM_COLS = 15 - } -} diff --git a/tv/src/main/java/net/activitywatch/android/Movie.kt b/tv/src/main/java/net/activitywatch/android/Movie.kt deleted file mode 100644 index d9e7007f..00000000 --- a/tv/src/main/java/net/activitywatch/android/Movie.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package net.activitywatch.android - -import java.io.Serializable - -/** - * Movie class represents video entity with title, description, image thumbs and video url. - */ -data class Movie( - var id: Long = 0, - var title: String? = null, - var description: String? = null, - var backgroundImageUrl: String? = null, - var cardImageUrl: String? = null, - var videoUrl: String? = null, - var studio: String? = null -) : Serializable { - - override fun toString(): String { - return "Movie{" + - "id=" + id + - ", title='" + title + '\'' + - ", videoUrl='" + videoUrl + '\'' + - ", backgroundImageUrl='" + backgroundImageUrl + '\'' + - ", cardImageUrl='" + cardImageUrl + '\'' + - '}' - } - - companion object { - internal const val serialVersionUID = 727566175075960653L - } -} diff --git a/tv/src/main/java/net/activitywatch/android/MovieList.kt b/tv/src/main/java/net/activitywatch/android/MovieList.kt deleted file mode 100644 index 3f769a63..00000000 --- a/tv/src/main/java/net/activitywatch/android/MovieList.kt +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package net.activitywatch.android - -object MovieList { - val MOVIE_CATEGORY = arrayOf( - "Category Zero", - "Category One", - "Category Two", - "Category Three", - "Category Four", - "Category Five" - ) - - val list: List by lazy { - setupMovies() - } - private var count: Long = 0 - - private fun setupMovies(): List { - val title = arrayOf( - "Zeitgeist 2010_ Year in Review", - "Google Demo Slam_ 20ft Search", - "Introducing Gmail Blue", - "Introducing Google Fiber to the Pole", - "Introducing Google Nose" - ) - - val description = "Fusce id nisi turpis. Praesent viverra bibendum semper. " + - "Donec tristique, orci sed semper lacinia, quam erat rhoncus massa, non congue tellus est " + - "quis tellus. Sed mollis orci venenatis quam scelerisque accumsan. Curabitur a massa sit " + - "amet mi accumsan mollis sed et magna. Vivamus sed aliquam risus. Nulla eget dolor in elit " + - "facilisis mattis. Ut aliquet luctus lacus. Phasellus nec commodo erat. Praesent tempus id " + - "lectus ac scelerisque. Maecenas pretium cursus lectus id volutpat." - val studio = arrayOf( - "Studio Zero", - "Studio One", - "Studio Two", - "Studio Three", - "Studio Four" - ) - val videoUrl = arrayOf( - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Zeitgeist/Zeitgeist%202010_%20Year%20in%20Review.mp4", - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%2020ft%20Search.mp4", - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Gmail%20Blue.mp4", - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Fiber%20to%20the%20Pole.mp4", - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Nose.mp4" - ) - val bgImageUrl = arrayOf( - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Zeitgeist/Zeitgeist%202010_%20Year%20in%20Review/bg.jpg", - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%2020ft%20Search/bg.jpg", - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Gmail%20Blue/bg.jpg", - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Fiber%20to%20the%20Pole/bg.jpg", - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Nose/bg.jpg" - ) - val cardImageUrl = arrayOf( - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Zeitgeist/Zeitgeist%202010_%20Year%20in%20Review/card.jpg", - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%2020ft%20Search/card.jpg", - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Gmail%20Blue/card.jpg", - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Fiber%20to%20the%20Pole/card.jpg", - "http://commondatastorage.googleapis.com/android-tv/Sample%20videos/April%20Fool's%202013/Introducing%20Google%20Nose/card.jpg" - ) - - val list = title.indices.map { - buildMovieInfo( - title[it], - description, - studio[it], - videoUrl[it], - cardImageUrl[it], - bgImageUrl[it] - ) - } - - return list - } - - private fun buildMovieInfo( - title: String, - description: String, - studio: String, - videoUrl: String, - cardImageUrl: String, - backgroundImageUrl: String - ): Movie { - val movie = Movie() - movie.id = count++ - movie.title = title - movie.description = description - movie.studio = studio - movie.cardImageUrl = cardImageUrl - movie.backgroundImageUrl = backgroundImageUrl - movie.videoUrl = videoUrl - return movie - } -} \ No newline at end of file diff --git a/tv/src/main/java/net/activitywatch/android/PlaybackActivity.kt b/tv/src/main/java/net/activitywatch/android/PlaybackActivity.kt deleted file mode 100644 index f5a989b9..00000000 --- a/tv/src/main/java/net/activitywatch/android/PlaybackActivity.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package net.activitywatch.android - -import android.os.Bundle -import android.support.v4.app.FragmentActivity - -/** Loads [PlaybackVideoFragment]. */ -class PlaybackActivity : FragmentActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - if (savedInstanceState == null) { - supportFragmentManager.beginTransaction() - .replace(android.R.id.content, PlaybackVideoFragment()) - .commit() - } - } -} \ No newline at end of file diff --git a/tv/src/main/java/net/activitywatch/android/PlaybackVideoFragment.kt b/tv/src/main/java/net/activitywatch/android/PlaybackVideoFragment.kt deleted file mode 100644 index 36f8800f..00000000 --- a/tv/src/main/java/net/activitywatch/android/PlaybackVideoFragment.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package net.activitywatch.android - -import android.net.Uri -import android.os.Bundle -import android.support.v17.leanback.app.VideoSupportFragment -import android.support.v17.leanback.app.VideoSupportFragmentGlueHost -import android.support.v17.leanback.media.MediaPlayerAdapter -import android.support.v17.leanback.media.PlaybackTransportControlGlue -import android.support.v17.leanback.widget.PlaybackControlsRow - -/** Handles video playback with media controls. */ -class PlaybackVideoFragment : VideoSupportFragment() { - - private lateinit var mTransportControlGlue: PlaybackTransportControlGlue - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val (_, title, description, _, _, videoUrl) = - activity?.intent?.getSerializableExtra(DetailsActivity.MOVIE) as Movie - - val glueHost = VideoSupportFragmentGlueHost(this@PlaybackVideoFragment) - val playerAdapter = MediaPlayerAdapter(context) - playerAdapter.setRepeatAction(PlaybackControlsRow.RepeatAction.INDEX_NONE) - - mTransportControlGlue = PlaybackTransportControlGlue(getActivity(), playerAdapter) - mTransportControlGlue.host = glueHost - mTransportControlGlue.title = title - mTransportControlGlue.subtitle = description - mTransportControlGlue.playWhenPrepared() - - playerAdapter.setDataSource(Uri.parse(videoUrl)) - } - - override fun onPause() { - super.onPause() - mTransportControlGlue.pause() - } -} \ No newline at end of file diff --git a/tv/src/main/java/net/activitywatch/android/VideoDetailsFragment.kt b/tv/src/main/java/net/activitywatch/android/VideoDetailsFragment.kt deleted file mode 100644 index ae1decfe..00000000 --- a/tv/src/main/java/net/activitywatch/android/VideoDetailsFragment.kt +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package net.activitywatch.android - -import android.content.Context -import android.content.Intent -import android.graphics.Bitmap -import android.os.Bundle -import android.support.v17.leanback.app.DetailsFragment -import android.support.v17.leanback.app.DetailsFragmentBackgroundController -import android.support.v17.leanback.widget.Action -import android.support.v17.leanback.widget.ArrayObjectAdapter -import android.support.v17.leanback.widget.ClassPresenterSelector -import android.support.v17.leanback.widget.DetailsOverviewRow -import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter -import android.support.v17.leanback.widget.FullWidthDetailsOverviewSharedElementHelper -import android.support.v17.leanback.widget.HeaderItem -import android.support.v17.leanback.widget.ImageCardView -import android.support.v17.leanback.widget.ListRow -import android.support.v17.leanback.widget.ListRowPresenter -import android.support.v17.leanback.widget.OnActionClickedListener -import android.support.v17.leanback.widget.OnItemViewClickedListener -import android.support.v17.leanback.widget.Presenter -import android.support.v17.leanback.widget.Row -import android.support.v17.leanback.widget.RowPresenter -import android.support.v4.app.ActivityOptionsCompat -import android.support.v4.content.ContextCompat -import android.util.Log -import android.widget.Toast - -import com.bumptech.glide.Glide -import com.bumptech.glide.load.resource.drawable.GlideDrawable -import com.bumptech.glide.request.animation.GlideAnimation -import com.bumptech.glide.request.target.SimpleTarget - -import java.util.Collections - -/** - * A wrapper fragment for leanback details screens. - * It shows a detailed view of video and its metadata plus related videos. - */ -class VideoDetailsFragment : DetailsFragment() { - - private var mSelectedMovie: Movie? = null - - private lateinit var mDetailsBackground: DetailsFragmentBackgroundController - private lateinit var mPresenterSelector: ClassPresenterSelector - private lateinit var mAdapter: ArrayObjectAdapter - - override fun onCreate(savedInstanceState: Bundle?) { - Log.d(TAG, "onCreate DetailsFragment") - super.onCreate(savedInstanceState) - - mDetailsBackground = DetailsFragmentBackgroundController(this) - - mSelectedMovie = activity.intent.getSerializableExtra(DetailsActivity.MOVIE) as Movie - if (mSelectedMovie != null) { - mPresenterSelector = ClassPresenterSelector() - mAdapter = ArrayObjectAdapter(mPresenterSelector) - setupDetailsOverviewRow() - setupDetailsOverviewRowPresenter() - setupRelatedMovieListRow() - adapter = mAdapter - initializeBackground(mSelectedMovie) - onItemViewClickedListener = ItemViewClickedListener() - } else { - val intent = Intent(context, MainActivity::class.java) - startActivity(intent) - } - } - - private fun initializeBackground(movie: Movie?) { - mDetailsBackground.enableParallax() - Glide.with(context) - .load(movie?.backgroundImageUrl) - .asBitmap() - .centerCrop() - .error(R.drawable.default_background) - .into>(object : SimpleTarget() { - override fun onResourceReady( - bitmap: Bitmap, - glideAnimation: GlideAnimation - ) { - mDetailsBackground.coverBitmap = bitmap - mAdapter.notifyArrayItemRangeChanged(0, mAdapter.size()) - } - }) - } - - private fun setupDetailsOverviewRow() { - Log.d(TAG, "doInBackground: " + mSelectedMovie?.toString()) - val row = DetailsOverviewRow(mSelectedMovie) - row.imageDrawable = ContextCompat.getDrawable(context, R.drawable.default_background) - val width = convertDpToPixel(context, DETAIL_THUMB_WIDTH) - val height = convertDpToPixel(context, DETAIL_THUMB_HEIGHT) - Glide.with(context) - .load(mSelectedMovie?.cardImageUrl) - .centerCrop() - .error(R.drawable.default_background) - .into>(object : SimpleTarget(width, height) { - override fun onResourceReady( - resource: GlideDrawable, - glideAnimation: GlideAnimation - ) { - Log.d(TAG, "details overview card image url ready: " + resource) - row.imageDrawable = resource - mAdapter.notifyArrayItemRangeChanged(0, mAdapter.size()) - } - }) - - val actionAdapter = ArrayObjectAdapter() - - actionAdapter.add( - Action( - ACTION_WATCH_TRAILER, - resources.getString(R.string.watch_trailer_1), - resources.getString(R.string.watch_trailer_2) - ) - ) - actionAdapter.add( - Action( - ACTION_RENT, - resources.getString(R.string.rent_1), - resources.getString(R.string.rent_2) - ) - ) - actionAdapter.add( - Action( - ACTION_BUY, - resources.getString(R.string.buy_1), - resources.getString(R.string.buy_2) - ) - ) - row.actionsAdapter = actionAdapter - - mAdapter.add(row) - } - - private fun setupDetailsOverviewRowPresenter() { - // Set detail background. - val detailsPresenter = FullWidthDetailsOverviewRowPresenter(DetailsDescriptionPresenter()) - detailsPresenter.backgroundColor = - ContextCompat.getColor(context, R.color.selected_background) - - // Hook up transition element. - val sharedElementHelper = FullWidthDetailsOverviewSharedElementHelper() - sharedElementHelper.setSharedElementEnterTransition( - activity, DetailsActivity.SHARED_ELEMENT_NAME - ) - detailsPresenter.setListener(sharedElementHelper) - detailsPresenter.isParticipatingEntranceTransition = true - - detailsPresenter.onActionClickedListener = OnActionClickedListener { action -> - if (action.id == ACTION_WATCH_TRAILER) { - val intent = Intent(context, PlaybackActivity::class.java) - intent.putExtra(DetailsActivity.MOVIE, mSelectedMovie) - startActivity(intent) - } else { - Toast.makeText(context, action.toString(), Toast.LENGTH_SHORT).show() - } - } - mPresenterSelector.addClassPresenter(DetailsOverviewRow::class.java, detailsPresenter) - } - - private fun setupRelatedMovieListRow() { - val subcategories = arrayOf(getString(R.string.related_movies)) - val list = MovieList.list - - Collections.shuffle(list) - val listRowAdapter = ArrayObjectAdapter(CardPresenter()) - for (j in 0 until NUM_COLS) { - listRowAdapter.add(list[j % 5]) - } - - val header = HeaderItem(0, subcategories[0]) - mAdapter.add(ListRow(header, listRowAdapter)) - mPresenterSelector.addClassPresenter(ListRow::class.java, ListRowPresenter()) - } - - private fun convertDpToPixel(context: Context, dp: Int): Int { - val density = context.applicationContext.resources.displayMetrics.density - return Math.round(dp.toFloat() * density) - } - - private inner class ItemViewClickedListener : OnItemViewClickedListener { - override fun onItemClicked( - itemViewHolder: Presenter.ViewHolder?, - item: Any?, - rowViewHolder: RowPresenter.ViewHolder, - row: Row - ) { - if (item is Movie) { - Log.d(TAG, "Item: " + item.toString()) - val intent = Intent(context, DetailsActivity::class.java) - intent.putExtra(resources.getString(R.string.movie), mSelectedMovie) - - val bundle = - ActivityOptionsCompat.makeSceneTransitionAnimation( - activity, - (itemViewHolder?.view as ImageCardView).mainImageView, - DetailsActivity.SHARED_ELEMENT_NAME - ) - .toBundle() - activity.startActivity(intent, bundle) - } - } - } - - companion object { - private val TAG = "VideoDetailsFragment" - - private val ACTION_WATCH_TRAILER = 1L - private val ACTION_RENT = 2L - private val ACTION_BUY = 3L - - private val DETAIL_THUMB_WIDTH = 274 - private val DETAIL_THUMB_HEIGHT = 274 - - private val NUM_COLS = 10 - } -} \ No newline at end of file diff --git a/tv/src/main/res/drawable/app_icon_your_company.png b/tv/src/main/res/drawable/app_icon_your_company.png deleted file mode 100644 index 0a47b018..00000000 Binary files a/tv/src/main/res/drawable/app_icon_your_company.png and /dev/null differ diff --git a/tv/src/main/res/drawable/default_background.xml b/tv/src/main/res/drawable/default_background.xml deleted file mode 100644 index 68e9ffd1..00000000 --- a/tv/src/main/res/drawable/default_background.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - \ No newline at end of file diff --git a/tv/src/main/res/drawable/movie.png b/tv/src/main/res/drawable/movie.png deleted file mode 100644 index cb5cb6d3..00000000 Binary files a/tv/src/main/res/drawable/movie.png and /dev/null differ diff --git a/tv/src/main/res/layout/activity_details.xml b/tv/src/main/res/layout/activity_details.xml deleted file mode 100644 index 83654422..00000000 --- a/tv/src/main/res/layout/activity_details.xml +++ /dev/null @@ -1,9 +0,0 @@ - - \ No newline at end of file diff --git a/tv/src/main/res/layout/activity_main.xml b/tv/src/main/res/layout/activity_main.xml deleted file mode 100644 index d57d1a29..00000000 --- a/tv/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,10 +0,0 @@ - - \ No newline at end of file diff --git a/tv/src/main/res/mipmap-hdpi/ic_launcher.png b/tv/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 898f3ed5..00000000 Binary files a/tv/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/tv/src/main/res/mipmap-mdpi/ic_launcher.png b/tv/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 64ba76f7..00000000 Binary files a/tv/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/tv/src/main/res/mipmap-xhdpi/ic_launcher.png b/tv/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index e5ed4659..00000000 Binary files a/tv/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/tv/src/main/res/mipmap-xxhdpi/ic_launcher.png b/tv/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index b0907cac..00000000 Binary files a/tv/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/tv/src/main/res/values/colors.xml b/tv/src/main/res/values/colors.xml deleted file mode 100644 index 6a9252f5..00000000 --- a/tv/src/main/res/values/colors.xml +++ /dev/null @@ -1,8 +0,0 @@ - - #000000 - #DDDDDD - #0096a6 - #ffaa3f - #ffaa3f - #3d3d3d - diff --git a/tv/src/main/res/values/strings.xml b/tv/src/main/res/values/strings.xml deleted file mode 100644 index 7270d07b..00000000 --- a/tv/src/main/res/values/strings.xml +++ /dev/null @@ -1,19 +0,0 @@ - - ActivityWatch - - Related Videos - Grid View - Error Fragment - Personal Settings - Watch trailer - FREE - Rent By Day - From $1.99 - Buy and Own - AT $9.99 - Movie - - - An error occurred - Dismiss - diff --git a/tv/src/main/res/values/styles.xml b/tv/src/main/res/values/styles.xml deleted file mode 100644 index 10ec6a5f..00000000 --- a/tv/src/main/res/values/styles.xml +++ /dev/null @@ -1,4 +0,0 @@ - - -