summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2020-02-12 00:08:06 +0100
committerJavier <dev.git@javispedro.com>2020-02-12 20:48:39 +0100
commitac329e140435c3488ea5dfef89d237e2325da9e4 (patch)
tree47f03d0ae7ef63f9649aed834c0110b47e9936b5 /app
downloadwallmotion-ac329e140435c3488ea5dfef89d237e2325da9e4.tar.gz
wallmotion-ac329e140435c3488ea5dfef89d237e2325da9e4.zip
initial import
Diffstat (limited to 'app')
-rw-r--r--app/.gitignore1
-rw-r--r--app/build.gradle32
-rw-r--r--app/proguard-rules.pro21
-rw-r--r--app/src/main/AndroidManifest.xml45
-rw-r--r--app/src/main/ic_launcher-web.pngbin0 -> 14353 bytes
-rw-r--r--app/src/main/java/com/javispedro/wallmotion/MainActivity.java117
-rw-r--r--app/src/main/java/com/javispedro/wallmotion/Renderer.java183
-rw-r--r--app/src/main/java/com/javispedro/wallmotion/SettingsActivity.java174
-rw-r--r--app/src/main/java/com/javispedro/wallmotion/WallService.java65
-rw-r--r--app/src/main/res/drawable-anydpi/ic_action_settings.xml11
-rw-r--r--app/src/main/res/drawable-anydpi/ic_action_wallpaper.xml11
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_settings.pngbin0 -> 457 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_wallpaper.pngbin0 -> 322 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_settings.pngbin0 -> 344 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_wallpaper.pngbin0 -> 214 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_settings.pngbin0 -> 611 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_wallpaper.pngbin0 -> 351 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_settings.pngbin0 -> 880 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_wallpaper.pngbin0 -> 519 bytes
-rw-r--r--app/src/main/res/drawable/ic_launcher_foreground.xml29
-rw-r--r--app/src/main/res/drawable/ic_logo.xml29
-rw-r--r--app/src/main/res/layout/activity_main.xml36
-rw-r--r--app/src/main/res/layout/settings_activity.xml9
-rw-r--r--app/src/main/res/menu/menu_main.xml11
-rw-r--r--app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml5
-rw-r--r--app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml5
-rw-r--r--app/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 1809 bytes
-rw-r--r--app/src/main/res/mipmap-hdpi/ic_launcher_round.pngbin0 -> 3506 bytes
-rw-r--r--app/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 1467 bytes
-rw-r--r--app/src/main/res/mipmap-mdpi/ic_launcher_round.pngbin0 -> 2398 bytes
-rw-r--r--app/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 2301 bytes
-rw-r--r--app/src/main/res/mipmap-xhdpi/ic_launcher_round.pngbin0 -> 4886 bytes
-rw-r--r--app/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 3499 bytes
-rw-r--r--app/src/main/res/mipmap-xxhdpi/ic_launcher_round.pngbin0 -> 7557 bytes
-rw-r--r--app/src/main/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 4540 bytes
-rw-r--r--app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.pngbin0 -> 10663 bytes
-rw-r--r--app/src/main/res/raw/video_file_error.mp4bin0 -> 17508 bytes
-rw-r--r--app/src/main/res/raw/video_file_missing.mp4bin0 -> 17253 bytes
-rw-r--r--app/src/main/res/values/arrays.xml11
-rw-r--r--app/src/main/res/values/colors.xml6
-rw-r--r--app/src/main/res/values/dimens.xml3
-rw-r--r--app/src/main/res/values/ic_launcher_background.xml4
-rw-r--r--app/src/main/res/values/strings.xml26
-rw-r--r--app/src/main/res/values/styles.xml20
-rw-r--r--app/src/main/res/xml/root_preferences.xml32
-rw-r--r--app/src/main/res/xml/wallpaper.xml6
-rwxr-xr-xapp/svgs/generate_video.sh7
-rw-r--r--app/svgs/video_file_error.pngbin0 -> 10587 bytes
-rw-r--r--app/svgs/video_file_error.svg92
-rw-r--r--app/svgs/video_file_missing.pngbin0 -> 10640 bytes
-rw-r--r--app/svgs/video_file_missing.svg44
-rw-r--r--app/svgs/videowall.svg82
52 files changed, 1117 insertions, 0 deletions
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..adb5b49
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.2"
+ defaultConfig {
+ applicationId "com.javispedro.wallmotion"
+ minSdkVersion 23
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'androidx.appcompat:appcompat:1.0.2'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'com.google.android.material:material:1.0.0'
+ implementation 'androidx.preference:preference:1.1.0-alpha05'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
+ implementation 'org.jetbrains:annotations-java5:15.0'
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# 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/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2d010dd
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.javispedro.wallmotion">
+
+ <uses-feature
+ android:name="android.software.live_wallpaper"
+ android:required="true" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+
+ <service
+ android:name=".WallService"
+ android:permission="android.permission.BIND_WALLPAPER">
+ <intent-filter>
+ <action android:name="android.service.wallpaper.WallpaperService" />
+ </intent-filter>
+ <meta-data android:name="android.service.wallpaper"
+ android:resource="@xml/wallpaper" />
+ </service>
+
+ <activity
+ android:name=".SettingsActivity"
+ android:label="@string/title_activity_settings"
+ android:exported="true">
+ </activity>
+
+ <activity
+ android:name=".MainActivity"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest> \ No newline at end of file
diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png
new file mode 100644
index 0000000..c80c1c7
--- /dev/null
+++ b/app/src/main/ic_launcher-web.png
Binary files differ
diff --git a/app/src/main/java/com/javispedro/wallmotion/MainActivity.java b/app/src/main/java/com/javispedro/wallmotion/MainActivity.java
new file mode 100644
index 0000000..c6859ae
--- /dev/null
+++ b/app/src/main/java/com/javispedro/wallmotion/MainActivity.java
@@ -0,0 +1,117 @@
+package com.javispedro.wallmotion;
+
+import android.app.WallpaperManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.Menu;
+import android.view.MenuItem;
+
+public class MainActivity extends AppCompatActivity {
+ private static final String TAG = "MainActivity";
+
+ private Renderer renderer;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ FloatingActionButton fab = findViewById(R.id.set_wallpaper);
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ openWallpaperSelector();
+ }
+ });
+
+ SurfaceView view = findViewById(R.id.wallpaper_view);
+ view.getHolder().addCallback(new WallpaperViewCallback());
+
+ renderer = new Renderer(this);
+ }
+
+ @Override
+ protected void onDestroy() {
+ renderer.stop();
+ renderer = null;
+
+ super.onDestroy();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // 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.
+ int id = item.getItemId();
+
+ if (id == R.id.action_settings) {
+ openSettingsActivity();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ renderer.stop();
+ super.onPause();
+ }
+
+ private class WallpaperViewCallback implements SurfaceHolder.Callback {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ Log.d(TAG, "surfaceCreated");
+ renderer.start(holder.getSurface());
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ Log.d(TAG, "surfaceDestroyed");
+ renderer.stop();
+ }
+ }
+
+ private void openSettingsActivity() {
+ Intent intent = new Intent(this, SettingsActivity.class);
+ startActivity(intent);
+ }
+
+ private void openWallpaperSelector() {
+ Intent intent = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
+ intent.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT,
+ new ComponentName(this, WallService.class));
+ startActivity(intent);
+ }
+}
diff --git a/app/src/main/java/com/javispedro/wallmotion/Renderer.java b/app/src/main/java/com/javispedro/wallmotion/Renderer.java
new file mode 100644
index 0000000..6730afe
--- /dev/null
+++ b/app/src/main/java/com/javispedro/wallmotion/Renderer.java
@@ -0,0 +1,183 @@
+package com.javispedro.wallmotion;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.SharedPreferences;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Surface;
+
+import androidx.preference.PreferenceManager;
+
+import java.io.IOException;
+
+public class Renderer extends ContextWrapper {
+ private static final String TAG = "Renderer";
+
+ private SharedPreferences prefs;
+ private MediaPlayer player;
+
+ private int savedPosition;
+
+ public Renderer(Context context) {
+ super(context);
+
+ prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.registerOnSharedPreferenceChangeListener(new PrefsListener());
+ }
+
+ public boolean isActive() {
+ return player != null;
+ }
+
+ public void start(Surface surface) {
+ preparePlayer(surface);
+ }
+
+ public void stop() {
+ if (player != null && player.isPlaying() && shouldSaveRestorePosition()) {
+ savedPosition = player.getCurrentPosition();
+ Log.d(TAG, "storing current position: " + savedPosition + " ms");
+ }
+ releasePlayer();
+ }
+
+ public void reset() {
+ releasePlayer();
+ savedPosition = 0;
+ }
+
+ private class PrefsListener implements SharedPreferences.OnSharedPreferenceChangeListener {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if (key.equals(getString(R.string.settings_video_file_key))) {
+ Log.d(TAG, "video file key changed");
+ reset();
+ } else if (key.equals(getString(R.string.settings_display_restart_key))) {
+ savedPosition = 0;
+ }
+ }
+ }
+
+ private class PlayerListener implements MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnSeekCompleteListener {
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ Log.e(TAG, "MediaPlayer error: " + what + " extra:" + extra);
+ return false;
+ }
+
+ @Override
+ public void onPrepared(MediaPlayer mp) {
+ Log.d(TAG, "onPrepared");
+ if (player != null && !player.isPlaying()) {
+ Log.d(TAG, "start playing");
+ if (savedPosition > 0 && shouldSaveRestorePosition()) {
+ restorePlayerPosition();
+ }
+ player.start();
+ }
+ }
+
+ @Override
+ public void onSeekComplete(MediaPlayer mp) {
+ Log.d(TAG, "onSeekComplete: " + mp.getCurrentPosition() + " ms");
+ }
+ }
+
+ private void preparePlayer(Surface surface) {
+ releasePlayer();
+
+ int scaling = getVideoScaling();
+ Log.d(TAG, "video scaling mode: " + scaling);
+
+ Uri uri = getVideoFileUri();
+ if (uri == null) {
+ Log.d(TAG, "no video URI: using missing video URI");
+ uri = getResourceVideoFileUri(R.raw.video_file_missing);
+ scaling = MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT;
+ }
+
+ try {
+ preparePlayer(surface, uri, scaling);
+ } catch (Exception ex) {
+ Log.e(TAG, "Could not open video URI: " + uri.toString() + " : " + ex.toString());
+ ex.printStackTrace();
+ releasePlayer();
+
+ uri = getResourceVideoFileUri(R.raw.video_file_error);
+ scaling = MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT;
+
+ try {
+ preparePlayer(surface, uri, scaling);
+ } catch (Exception ex2) {
+ Log.e(TAG, "Could not open error video URI either: " + ex2.toString());
+ ex2.printStackTrace();
+ releasePlayer();
+ }
+ }
+ }
+
+ private void preparePlayer(Surface surface, Uri uri, int scaling) throws IOException {
+ Log.d(TAG, "creating player");
+ player = new MediaPlayer();
+ PlayerListener listener = new PlayerListener();
+ player.setOnErrorListener(listener);
+ player.setOnPreparedListener(listener);
+ player.setOnSeekCompleteListener(listener);
+ player.setLooping(true);
+ player.setVolume(0, 0);
+ player.setSurface(surface);
+ Log.d(TAG, "setting data source to " + uri.toString());
+ player.setDataSource(this, uri);
+ player.setVideoScalingMode(scaling);
+ player.prepareAsync();
+ }
+
+ private void releasePlayer() {
+ if (player != null) {
+ Log.d(TAG, "releasing player");
+ player.release();
+ player = null;
+ }
+ }
+
+ private void restorePlayerPosition() {
+ Log.d(TAG, "seeking to " + savedPosition + " ms");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ player.seekTo(savedPosition, MediaPlayer.SEEK_CLOSEST);
+ } else {
+ player.seekTo(savedPosition);
+ }
+ }
+
+ private Uri getVideoFileUri() {
+ String new_value = prefs.getString(getString(R.string.settings_video_file_key), null);
+ if (new_value != null) {
+ return Uri.parse(new_value);
+ } else {
+ return null;
+ }
+ }
+
+ private Uri getResourceVideoFileUri(int resId) {
+ Uri.Builder builder = new Uri.Builder();
+ builder.scheme("android.resource").authority(getPackageName()).appendPath(Integer.toString(resId));
+ return builder.build();
+ }
+
+ private int getVideoScaling() {
+ String value = prefs.getString(getString(R.string.settings_display_scale_key), null);
+ if (TextUtils.equals(value, getString(R.string.settings_display_scale_to_fit_with_cropping_key))) {
+ return MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING;
+ } else {
+ return MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT;
+ }
+ }
+
+ private boolean shouldSaveRestorePosition() {
+ return !prefs.getBoolean(getString(R.string.settings_display_restart_key), false);
+ }
+}
diff --git a/app/src/main/java/com/javispedro/wallmotion/SettingsActivity.java b/app/src/main/java/com/javispedro/wallmotion/SettingsActivity.java
new file mode 100644
index 0000000..6919ea1
--- /dev/null
+++ b/app/src/main/java/com/javispedro/wallmotion/SettingsActivity.java
@@ -0,0 +1,174 @@
+package com.javispedro.wallmotion;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.OpenableColumns;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceManager;
+
+public class SettingsActivity extends AppCompatActivity {
+ private static final String TAG = "SettingsActivity";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.settings_activity);
+ getSupportFragmentManager()
+ .beginTransaction()
+ .replace(R.id.settings, new SettingsFragment())
+ .commit();
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ @Override
+ public boolean onSupportNavigateUp() {
+ onBackPressed();
+ return true;
+ }
+
+ public static class SettingsFragment extends PreferenceFragmentCompat {
+ private final static int PICK_VIDEO_FILE = 1;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ setPreferencesFromResource(R.xml.root_preferences, rootKey);
+ Preference video_file = findPreference(getString(R.string.settings_video_file_key));
+ video_file.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("video/*");
+ intent.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+ startActivityForResult(intent, PICK_VIDEO_FILE);
+ return true;
+ }
+ });
+ video_file.setSummary(getVideoFileSummary());
+ Preference video_file_clear = findPreference(getString(R.string.settings_video_file_clear_key));
+ video_file_clear.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ setVideoFile(null);
+ return true;
+ }
+ });
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode,
+ Intent resultData) {
+ switch (requestCode) {
+ case PICK_VIDEO_FILE:
+ if (resultCode == Activity.RESULT_OK && resultData != null) {
+ Uri uri = resultData.getData();
+ setVideoFile(uri);
+ }
+ break;
+ }
+ }
+
+ private void setVideoFile(Uri uri) {
+ ContentResolver resolver = getContext().getContentResolver();
+
+ // First, release any permission request on the current uri
+ String cur_value = getStringPref(getString(R.string.settings_video_file_key));
+ if (!TextUtils.isEmpty(cur_value)) {
+ Uri cur_uri = Uri.parse(cur_value);
+ Log.d(TAG, "release persistable uri permission on uri: " + cur_uri.toString());
+ try {
+ resolver.releasePersistableUriPermission(cur_uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } catch (Exception ex) {
+ Log.w(TAG, "could not release persistable uri permission on uri: " + cur_uri.toString());
+ ex.printStackTrace();
+ }
+ }
+
+ // Then, store the new setting
+ if (uri != null) {
+ Log.d(TAG, "storing video_file pref uri: " + uri.toString());
+ setStringPref(getString(R.string.settings_video_file_key), uri.toString());
+
+ // Take a persistent permission on it
+ Log.d(TAG, "take persistable uri permission on uri: " + uri.toString());
+ resolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } else {
+ Log.d(TAG, "clearing video_file pref uri");
+ clearPref(getString(R.string.settings_video_file_key));
+ }
+
+ // Refresh the UI summary
+ Preference video_file = findPreference(getString(R.string.settings_video_file_key));
+ video_file.setSummary(getVideoFileSummary());
+ }
+
+ private String getVideoFileSummary() {
+ String cur_value = getStringPref(getString(R.string.settings_video_file_key));
+ if (!TextUtils.isEmpty(cur_value)) {
+ ContentResolver resolver = getContext().getContentResolver();
+ Uri uri = Uri.parse(cur_value);
+ String[] projection = {OpenableColumns.DISPLAY_NAME};
+ try {
+ Cursor cursor = resolver.query(uri, projection, null, null, null);
+ if (cursor != null) {
+ int col = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
+ if (col >= 0 && cursor.moveToFirst()) {
+ String name = cursor.getString(col);
+ cursor.close();
+ Log.d(TAG, "got video file display_name: " + name);
+ return name;
+ } else {
+ Log.w(TAG, "Could not navigate cursor for URI: " + uri.toString());
+ }
+ cursor.close();
+ } else {
+ Log.w(TAG, "Could not get cursor for URI: " + uri.toString());
+ }
+ } catch (java.lang.SecurityException ex) {
+ ex.printStackTrace();
+ Log.w(TAG, "Security exception while reading URI: " + uri.toString());
+ }
+ return uri.getLastPathSegment();
+ }
+ return getString(R.string.settings_not_set);
+ }
+
+ protected String getStringPref(String pref) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ return prefs.getString(pref, null);
+ }
+
+ protected void setStringPref(String pref, String new_value) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ String cur_value = prefs.getString(pref, null);
+ if (!TextUtils.equals(cur_value, new_value)) {
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(pref, new_value);
+ editor.apply();
+ }
+ }
+
+ protected void clearPref(String pref) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ if (prefs.contains(pref)) {
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.remove(pref);
+ editor.apply();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/com/javispedro/wallmotion/WallService.java b/app/src/main/java/com/javispedro/wallmotion/WallService.java
new file mode 100644
index 0000000..6a004ff
--- /dev/null
+++ b/app/src/main/java/com/javispedro/wallmotion/WallService.java
@@ -0,0 +1,65 @@
+package com.javispedro.wallmotion;
+
+import android.service.wallpaper.WallpaperService;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+public class WallService extends WallpaperService {
+ private static final String TAG = "WallService";
+
+ @Override
+ public Engine onCreateEngine() {
+ return new WallpaperEngine();
+ }
+
+ private class WallpaperEngine extends Engine {
+ private static final String TAG = "WallpaperEngine";
+
+ private Renderer renderer;
+
+ @Override
+ public void onCreate(SurfaceHolder surfaceHolder) {
+ Log.d(TAG, "onCreate");
+ renderer = new Renderer(WallService.this);
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d(TAG, "onDestroy");
+ renderer.stop();
+ renderer = null;
+ }
+
+ @Override
+ public void onSurfaceDestroyed(SurfaceHolder holder) {
+ Log.d(TAG, "onSurfaceDestroyed");
+ renderer.stop();
+ }
+
+ @Override
+ public void onSurfaceCreated(SurfaceHolder holder) {
+ Log.d(TAG, "onSurfaceCreated");
+ }
+
+ @Override
+ public void onSurfaceChanged(SurfaceHolder holder, int format,
+ int width, int height) {
+ Log.d(TAG, "onSurfaceChanged");
+ }
+
+ @Override
+ public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
+ Log.d(TAG, "onSurfaceRedrawNeeded");
+ }
+
+ @Override
+ public void onVisibilityChanged(boolean visible) {
+ Log.d(TAG, "onVisibilityChanged(visible: " + visible + ")");
+ if (visible) {
+ renderer.start(getSurfaceHolder().getSurface());
+ } else {
+ renderer.stop();
+ }
+ }
+ }
+}
diff --git a/app/src/main/res/drawable-anydpi/ic_action_settings.xml b/app/src/main/res/drawable-anydpi/ic_action_settings.xml
new file mode 100644
index 0000000..01b2fba
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_action_settings.xml
@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="#FFFFFF"
+ android:alpha="0.8">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z"/>
+</vector>
diff --git a/app/src/main/res/drawable-anydpi/ic_action_wallpaper.xml b/app/src/main/res/drawable-anydpi/ic_action_wallpaper.xml
new file mode 100644
index 0000000..028d51e
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_action_wallpaper.xml
@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="#FFFFFF"
+ android:alpha="0.8">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,4h7L11,2L4,2c-1.1,0 -2,0.9 -2,2v7h2L4,4zM10,13l-4,5h12l-3,-4 -2.03,2.71L10,13zM17,8.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S14,7.67 14,8.5s0.67,1.5 1.5,1.5S17,9.33 17,8.5zM20,2h-7v2h7v7h2L22,4c0,-1.1 -0.9,-2 -2,-2zM20,20h-7v2h7c1.1,0 2,-0.9 2,-2v-7h-2v7zM4,13L2,13v7c0,1.1 0.9,2 2,2h7v-2L4,20v-7z"/>
+</vector>
diff --git a/app/src/main/res/drawable-hdpi/ic_action_settings.png b/app/src/main/res/drawable-hdpi/ic_action_settings.png
new file mode 100644
index 0000000..a99894b
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_action_settings.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_wallpaper.png b/app/src/main/res/drawable-hdpi/ic_action_wallpaper.png
new file mode 100644
index 0000000..941f061
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_action_wallpaper.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_settings.png b/app/src/main/res/drawable-mdpi/ic_action_settings.png
new file mode 100644
index 0000000..69c67dd
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_action_settings.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_wallpaper.png b/app/src/main/res/drawable-mdpi/ic_action_wallpaper.png
new file mode 100644
index 0000000..740fb8a
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_action_wallpaper.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_settings.png b/app/src/main/res/drawable-xhdpi/ic_action_settings.png
new file mode 100644
index 0000000..3d88a9e
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_action_settings.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_wallpaper.png b/app/src/main/res/drawable-xhdpi/ic_action_wallpaper.png
new file mode 100644
index 0000000..1547cac
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_action_wallpaper.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_settings.png b/app/src/main/res/drawable-xxhdpi/ic_action_settings.png
new file mode 100644
index 0000000..80e49bb
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_settings.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_wallpaper.png b/app/src/main/res/drawable-xxhdpi/ic_action_wallpaper.png
new file mode 100644
index 0000000..45a7f14
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_wallpaper.png
Binary files differ
diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..e0b9f70
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,29 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="m4,4h7V2H4C2.9,2 2,2.9 2,4v7h2z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="m20,2h-7v2h7v7h2V4C22,2.9 21.1,2 20,2Z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="m20,20h-7v2h7c1.1,0 2,-0.9 2,-2v-7h-2z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,13H2v7c0,1.1 0.9,2 2,2h7V20H4Z"/>
+ <path
+ android:pathData="M16.978,4.5339L16.978,6.2133L15.2986,6.2133L15.2986,4.5339L8.5811,4.5339L8.5811,6.2133L6.9017,6.2133L6.9017,4.5339L5.2223,4.5339L5.2223,19.6483h1.6794v-1.6794h1.6794v1.6794h6.7175v-1.6794h1.6794v1.6794h1.6794L18.6573,4.5339ZM8.5811,16.2895L6.9017,16.2895v-1.6794h1.6794zM8.5811,12.9308L6.9017,12.9308v-1.6794h1.6794zM8.5811,9.572L6.9017,9.572L6.9017,7.8927h1.6794zM16.978,16.2895h-1.6794v-1.6794h1.6794zM16.978,12.9308h-1.6794v-1.6794h1.6794zM16.978,9.572L15.2986,9.572L15.2986,7.8927h1.6794z"
+ android:strokeWidth="0.83968925"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="m10.061,12.3404 l-3.1593,3.9492h9.478l-2.3695,-3.1593 -1.6034,2.1404z"
+ android:strokeWidth="0.78983057"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="m14.5593,8.8051c0,-0.83 -0.67,-1.5 -1.5,-1.5 -0.83,0 -1.5,0.67 -1.5,1.5 0,0.83 0.67,1.5 1.5,1.5 0.83,0 1.5,-0.67 1.5,-1.5z"
+ android:fillColor="#ffffff"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_logo.xml b/app/src/main/res/drawable/ic_logo.xml
new file mode 100644
index 0000000..4eb6d06
--- /dev/null
+++ b/app/src/main/res/drawable/ic_logo.xml
@@ -0,0 +1,29 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="m4,4h7V2H4C2.9,2 2,2.9 2,4v7h2z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="m20,2h-7v2h7v7h2V4C22,2.9 21.1,2 20,2Z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="m20,20h-7v2h7c1.1,0 2,-0.9 2,-2v-7h-2z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,13H2v7c0,1.1 0.9,2 2,2h7V20H4Z"/>
+ <path
+ android:pathData="M16.978,4.5339L16.978,6.2133L15.2986,6.2133L15.2986,4.5339L8.5811,4.5339L8.5811,6.2133L6.9017,6.2133L6.9017,4.5339L5.2223,4.5339L5.2223,19.6483h1.6794v-1.6794h1.6794v1.6794h6.7175v-1.6794h1.6794v1.6794h1.6794L18.6573,4.5339ZM8.5811,16.2895L6.9017,16.2895v-1.6794h1.6794zM8.5811,12.9308L6.9017,12.9308v-1.6794h1.6794zM8.5811,9.572L6.9017,9.572L6.9017,7.8927h1.6794zM16.978,16.2895h-1.6794v-1.6794h1.6794zM16.978,12.9308h-1.6794v-1.6794h1.6794zM16.978,9.572L15.2986,9.572L15.2986,7.8927h1.6794z"
+ android:strokeWidth="0.83968925"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="m10.061,12.3404 l-3.1593,3.9492h9.478l-2.3695,-3.1593 -1.6034,2.1404z"
+ android:strokeWidth="0.78983057"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="m14.5593,8.8051c0,-0.83 -0.67,-1.5 -1.5,-1.5 -0.83,0 -1.5,0.67 -1.5,1.5 0,0.83 0.67,1.5 1.5,1.5 0.83,0 1.5,-0.67 1.5,-1.5z"
+ android:fillColor="#ffffff"/>
+</vector>
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..f195562
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity">
+
+ <SurfaceView
+ android:id="@+id/wallpaper_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <com.google.android.material.appbar.AppBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="@style/AppTheme.AppBarOverlay">
+
+ <androidx.appcompat.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="?attr/colorPrimary"
+ app:popupTheme="@style/AppTheme.PopupOverlay" />
+
+ </com.google.android.material.appbar.AppBarLayout>
+
+ <com.google.android.material.floatingactionbutton.FloatingActionButton
+ android:id="@+id/set_wallpaper"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|end"
+ android:layout_margin="@dimen/fab_margin"
+ app:srcCompat="@drawable/ic_action_wallpaper" />
+
+</androidx.coordinatorlayout.widget.CoordinatorLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/settings_activity.xml b/app/src/main/res/layout/settings_activity.xml
new file mode 100644
index 0000000..de6591a
--- /dev/null
+++ b/app/src/main/res/layout/settings_activity.xml
@@ -0,0 +1,9 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <FrameLayout
+ android:id="@+id/settings"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..1f5570a
--- /dev/null
+++ b/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,11 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:context="com.javispedro.wallmotion.MainActivity">
+ <item
+ android:id="@+id/action_settings"
+ android:icon="@drawable/ic_action_settings"
+ android:orderInCategory="100"
+ android:title="@string/action_settings"
+ app:showAsAction="ifRoom" />
+</menu>
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..7353dbd
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/ic_launcher_background"/>
+ <foreground android:drawable="@drawable/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..7353dbd
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/ic_launcher_background"/>
+ <foreground android:drawable="@drawable/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..9045b44
--- /dev/null
+++ b/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..3def45d
--- /dev/null
+++ b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Binary files differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..78e6f12
--- /dev/null
+++ b/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..c7f9915
--- /dev/null
+++ b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..dd4c9e1
--- /dev/null
+++ b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..4ee79f8
--- /dev/null
+++ b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..553d0d3
--- /dev/null
+++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..895d9eb
--- /dev/null
+++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..5baec4f
--- /dev/null
+++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..c5998f7
--- /dev/null
+++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/app/src/main/res/raw/video_file_error.mp4 b/app/src/main/res/raw/video_file_error.mp4
new file mode 100644
index 0000000..64b8c83
--- /dev/null
+++ b/app/src/main/res/raw/video_file_error.mp4
Binary files differ
diff --git a/app/src/main/res/raw/video_file_missing.mp4 b/app/src/main/res/raw/video_file_missing.mp4
new file mode 100644
index 0000000..050dc56
--- /dev/null
+++ b/app/src/main/res/raw/video_file_missing.mp4
Binary files differ
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
new file mode 100644
index 0000000..073ac70
--- /dev/null
+++ b/app/src/main/res/values/arrays.xml
@@ -0,0 +1,11 @@
+<resources>
+ <!-- Settings display zoom values -->
+ <string-array name="settings_display_scale_entries">
+ <item>Stretch to fit</item>
+ <item>Scale and crop to fit</item>
+ </string-array>
+ <string-array name="settings_display_scale_values">
+ <item>@string/settings_display_scale_to_fit_key</item>
+ <item>@string/settings_display_scale_to_fit_with_cropping_key</item>
+ </string-array>
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..69b2233
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#008577</color>
+ <color name="colorPrimaryDark">#00574B</color>
+ <color name="colorAccent">#D81B60</color>
+</resources>
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..59a0b0c
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,3 @@
+<resources>
+ <dimen name="fab_margin">16dp</dimen>
+</resources>
diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 0000000..2a54660
--- /dev/null
+++ b/app/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="ic_launcher_background">#A4A633</color>
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a8111a0
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,26 @@
+<resources>
+ <string name="app_name">Wallmotion</string>
+
+ <string name="wallpaper_description">Use a video file as wallpaper</string>
+
+ <!-- Main activity -->
+ <string name="action_settings">Settings</string>
+
+ <!-- Preferences -->
+ <string name="title_activity_settings">Wallmotion settings</string>
+ <string name="settings_source_header">Source video</string>
+ <string name="settings_video_file_key">video_file</string>
+ <string name="settings_video_file_title">Video file</string>
+ <string name="settings_video_file_clear_key">video_file_clear</string>
+ <string name="settings_video_file_clear_title">Reset to (none)</string>
+ <string name="settings_not_set">(None)</string>
+ <string name="settings_display_header">Display options</string>
+ <string name="settings_display_scale_key">scale</string>
+ <string name="settings_display_scale_title">Scaling</string>
+ <string name="settings_display_scale_to_fit_key">scale_to_fit</string>
+ <string name="settings_display_scale_to_fit_with_cropping_key">scale_to_fit_with_cropping</string>
+ <string name="settings_display_restart_key">restart</string>
+ <string name="settings_display_restart_title">Restart video on each display</string>
+ <string name="settings_display_restart_title_on">Video will be restarted every time wallpaper is shown</string>
+ <string name="settings_display_restart_title_off">Video time will be remembered every time wallpaper is shown</string>
+</resources>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..545b9c6
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+ </style>
+
+ <style name="AppTheme.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+
+ <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+
+</resources>
diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml
new file mode 100644
index 0000000..14b05d7
--- /dev/null
+++ b/app/src/main/res/xml/root_preferences.xml
@@ -0,0 +1,32 @@
+<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <PreferenceCategory app:title="@string/settings_source_header">
+ <Preference
+ app:key="@string/settings_video_file_key"
+ app:title="@string/settings_video_file_title"
+ app:persistent="true" />
+ <Preference
+ app:key="@string/settings_video_file_clear_key"
+ app:title="@string/settings_video_file_clear_title"
+ />
+ </PreferenceCategory>
+
+ <PreferenceCategory app:title="@string/settings_display_header">
+ <ListPreference
+ app:key="@string/settings_display_scale_key"
+ app:title="@string/settings_display_scale_title"
+ app:entries="@array/settings_display_scale_entries"
+ app:entryValues="@array/settings_display_scale_values"
+ app:defaultValue="@string/settings_display_scale_to_fit_key"
+ app:useSimpleSummaryProvider="true"
+ app:persistent="true" />
+ <SwitchPreferenceCompat
+ app:key="@string/settings_display_restart_key"
+ app:title="@string/settings_display_restart_title"
+ app:summaryOn="@string/settings_display_restart_title_on"
+ app:summaryOff="@string/settings_display_restart_title_off"
+ app:persistent="true"
+ />
+ </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/wallpaper.xml b/app/src/main/res/xml/wallpaper.xml
new file mode 100644
index 0000000..d4af285
--- /dev/null
+++ b/app/src/main/res/xml/wallpaper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"
+ android:description="@string/wallpaper_description"
+ android:settingsActivity="com.javispedro.wallmotion.SettingsActivity"
+ android:thumbnail="@drawable/ic_logo"
+ />
diff --git a/app/svgs/generate_video.sh b/app/svgs/generate_video.sh
new file mode 100755
index 0000000..450a7eb
--- /dev/null
+++ b/app/svgs/generate_video.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+for i in video_file_missing video_file_error; do
+ ffmpeg -stream_loop 16 -i $i.png -vc libx264 -pix_fmt yuv420p -vprofile baseline -vf setpts=N/TB -movflags +faststart $i.mp4
+done
+
+
diff --git a/app/svgs/video_file_error.png b/app/svgs/video_file_error.png
new file mode 100644
index 0000000..fae4451
--- /dev/null
+++ b/app/svgs/video_file_error.png
Binary files differ
diff --git a/app/svgs/video_file_error.svg b/app/svgs/video_file_error.svg
new file mode 100644
index 0000000..adee487
--- /dev/null
+++ b/app/svgs/video_file_error.svg
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="26"
+ viewBox="0 0 26 26"
+ width="26"
+ version="1.1"
+ id="svg116"
+ sodipodi:docname="video_file_error.svg"
+ inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+ inkscape:export-xdpi="1890.4615"
+ inkscape:export-ydpi="1890.4615">
+ <metadata
+ id="metadata122">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs120" />
+ <sodipodi:namedview
+ pagecolor="#000000"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="3708"
+ inkscape:window-height="2032"
+ id="namedview118"
+ showgrid="false"
+ inkscape:snap-page="true"
+ inkscape:snap-center="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-bbox="true"
+ inkscape:bbox-paths="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="false"
+ inkscape:snap-text-baseline="true"
+ inkscape:zoom="21.05"
+ inkscape:cx="-7.8574822"
+ inkscape:cy="10.342829"
+ inkscape:window-x="132"
+ inkscape:window-y="54"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg116"
+ inkscape:pagecheckerboard="false" />
+ <rect
+ style="fill:#000000;stroke-width:1.0620501"
+ id="rect4719"
+ width="31.130642"
+ height="30.750593"
+ x="-2.5653207"
+ y="-1.615202" />
+ <path
+ d="m 19.090649,5.0023748 2,4 h -3 l -2,-4 h -2 l 2,4 h -3 l -2,-4 H 9.0906487 l 2.0000003,4 H 8.0906487 l -2,-4 h -1 c -1.1,0 -1.99,0.9 -1.99,2 l -0.01,12.0000002 c 0,1.1 0.9,2 2,2 H 21.090649 c 1.1,0 2,-0.9 2,-2 V 5.0023748 Z"
+ id="path112"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <g
+ id="g4785"
+ transform="matrix(0.50000001,0,0,0.50000001,7.0735941,9.0023748)">
+ <path
+ id="path4771"
+ d="M 0,0 H 24 V 24 H 0 Z"
+ inkscape:connector-curvature="0"
+ style="fill:none" />
+ <path
+ id="path4773"
+ d="M 12,2 C 6.48,2 2,6.48 2,12 2,17.52 6.48,22 12,22 17.52,22 22,17.52 22,12 22,6.48 17.52,2 12,2 Z m 1,15 h -2 v -2 h 2 z m 0,-4 H 11 V 7 h 2 z"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/app/svgs/video_file_missing.png b/app/svgs/video_file_missing.png
new file mode 100644
index 0000000..51d3e79
--- /dev/null
+++ b/app/svgs/video_file_missing.png
Binary files differ
diff --git a/app/svgs/video_file_missing.svg b/app/svgs/video_file_missing.svg
new file mode 100644
index 0000000..0291c75
--- /dev/null
+++ b/app/svgs/video_file_missing.svg
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ id="svg116"
+ version="1.1"
+ width="26"
+ viewBox="0 0 26 26"
+ height="26">
+ <metadata
+ id="metadata122">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs120" />
+ <path
+ id="path112"
+ d="m 19.090649,5.0023748 2,4 h -3 l -2,-4 h -2 l 2,4 h -3 l -2,-4 H 9.0906487 l 2.0000003,4 H 8.0906487 l -2,-4 h -1 c -1.1,0 -1.99,0.9 -1.99,2 l -0.01,12.0000002 c 0,1.1 0.9,2 2,2 H 21.090649 c 1.1,0 2,-0.9 2,-2 V 5.0023748 Z" />
+ <flowRoot
+ transform="matrix(0.35923173,0,0,0.35923173,0.90313202,6.2805158)"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto weight=255';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none"
+ id="flowRoot124"
+ xml:space="preserve"><flowRegion
+ style="font-style:normal;font-variant:normal;font-stretch:normal;font-family:Roboto;-inkscape-font-specification:'Roboto weight=255';text-align:center;text-anchor:middle;fill:#ffffff"
+ id="flowRegion126"><rect
+ style="font-style:normal;font-variant:normal;font-stretch:normal;font-family:Roboto;-inkscape-font-specification:'Roboto weight=255';text-align:center;text-anchor:middle;fill:#ffffff"
+ y="1.5866983"
+ x="6.223278"
+ height="55.106888"
+ width="56.722092"
+ id="rect128" /></flowRegion><flowPara
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Roboto;-inkscape-font-specification:'Roboto Bold';text-align:center;text-anchor:middle;fill:#ffffff"
+ id="flowPara130">?</flowPara></flowRoot></svg>
diff --git a/app/svgs/videowall.svg b/app/svgs/videowall.svg
new file mode 100644
index 0000000..e1f6367
--- /dev/null
+++ b/app/svgs/videowall.svg
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="24"
+ viewBox="0 0 24 24"
+ width="24"
+ version="1.1"
+ id="svg898"
+ sodipodi:docname="videowall.svg"
+ inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+ <metadata
+ id="metadata904">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs902" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="3708"
+ inkscape:window-height="2032"
+ id="namedview900"
+ showgrid="false"
+ inkscape:zoom="9.8333333"
+ inkscape:cx="-30.508475"
+ inkscape:cy="9.9775549"
+ inkscape:window-x="132"
+ inkscape:window-y="54"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg898" />
+ <path
+ d="m 4,4 h 7 V 2 H 4 C 2.9,2 2,2.9 2,4 v 7 h 2 z"
+ id="path1474" />
+ <path
+ d="m 20,2 h -7 v 2 h 7 v 7 h 2 V 4 C 22,2.9 21.1,2 20,2 Z"
+ id="path1468" />
+ <path
+ d="m 20,20 h -7 v 2 h 7 c 1.1,0 2,-0.9 2,-2 v -7 h -2 z"
+ id="path1466" />
+ <path
+ d="M 4,13 H 2 v 7 c 0,1.1 0.9,2 2,2 h 7 V 20 H 4 Z"
+ id="path894" />
+ <path
+ d="M0 0h24v24H0z"
+ fill="none"
+ id="path896" />
+ <path
+ inkscape:connector-curvature="0"
+ d="M 16.977966,4.5338995 V 6.213278 H 15.298587 V 4.5338995 H 8.581073 V 6.213278 H 6.901695 V 4.5338995 H 5.222316 V 19.648305 h 1.679379 v -1.679377 h 1.679378 v 1.679377 h 6.717514 v -1.679377 h 1.679379 v 1.679377 h 1.679379 V 4.5338995 Z M 8.581073,16.289548 H 6.901695 v -1.679379 h 1.679378 z m 0,-3.358757 H 6.901695 v -1.679377 h 1.679378 z m 0,-3.358755 H 6.901695 V 7.892657 h 1.679378 z m 8.396893,6.717512 h -1.679379 v -1.679379 h 1.679379 z m 0,-3.358757 h -1.679379 v -1.679377 h 1.679379 z m 0,-3.358755 H 15.298587 V 7.892657 h 1.679379 z"
+ id="path882"
+ style="fill:#000000;stroke-width:0.83968925" />
+ <path
+ d="m 10.061018,12.340395 -3.159323,3.949153 h 9.477966 l -2.369492,-3.159323 -1.603356,2.140441 z"
+ id="path1472"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;stroke-width:0.78983057" />
+ <path
+ d="m 14.559322,8.8050847 c 0,-0.83 -0.67,-1.5 -1.5,-1.5 -0.83,0 -1.5,0.67 -1.5,1.5 0,0.83 0.67,1.5000003 1.5,1.5000003 0.83,0 1.5,-0.6700003 1.5,-1.5000003 z"
+ id="path1470"
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0" />
+</svg>