From ac329e140435c3488ea5dfef89d237e2325da9e4 Mon Sep 17 00:00:00 2001 From: Javier Date: Wed, 12 Feb 2020 00:08:06 +0100 Subject: initial import --- app/.gitignore | 1 + app/build.gradle | 32 ++++ app/proguard-rules.pro | 21 +++ app/src/main/AndroidManifest.xml | 45 +++++ app/src/main/ic_launcher-web.png | Bin 0 -> 14353 bytes .../com/javispedro/wallmotion/MainActivity.java | 117 +++++++++++++ .../java/com/javispedro/wallmotion/Renderer.java | 183 +++++++++++++++++++++ .../javispedro/wallmotion/SettingsActivity.java | 174 ++++++++++++++++++++ .../com/javispedro/wallmotion/WallService.java | 65 ++++++++ .../res/drawable-anydpi/ic_action_settings.xml | 11 ++ .../res/drawable-anydpi/ic_action_wallpaper.xml | 11 ++ .../main/res/drawable-hdpi/ic_action_settings.png | Bin 0 -> 457 bytes .../main/res/drawable-hdpi/ic_action_wallpaper.png | Bin 0 -> 322 bytes .../main/res/drawable-mdpi/ic_action_settings.png | Bin 0 -> 344 bytes .../main/res/drawable-mdpi/ic_action_wallpaper.png | Bin 0 -> 214 bytes .../main/res/drawable-xhdpi/ic_action_settings.png | Bin 0 -> 611 bytes .../res/drawable-xhdpi/ic_action_wallpaper.png | Bin 0 -> 351 bytes .../res/drawable-xxhdpi/ic_action_settings.png | Bin 0 -> 880 bytes .../res/drawable-xxhdpi/ic_action_wallpaper.png | Bin 0 -> 519 bytes .../main/res/drawable/ic_launcher_foreground.xml | 29 ++++ app/src/main/res/drawable/ic_logo.xml | 29 ++++ app/src/main/res/layout/activity_main.xml | 36 ++++ app/src/main/res/layout/settings_activity.xml | 9 + app/src/main/res/menu/menu_main.xml | 11 ++ app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../res/mipmap-anydpi-v26/ic_launcher_round.xml | 5 + app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 1809 bytes app/src/main/res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 3506 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1467 bytes app/src/main/res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2398 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 2301 bytes .../main/res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 4886 bytes app/src/main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 3499 bytes .../main/res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 7557 bytes app/src/main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 4540 bytes .../main/res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 10663 bytes app/src/main/res/raw/video_file_error.mp4 | Bin 0 -> 17508 bytes app/src/main/res/raw/video_file_missing.mp4 | Bin 0 -> 17253 bytes app/src/main/res/values/arrays.xml | 11 ++ app/src/main/res/values/colors.xml | 6 + app/src/main/res/values/dimens.xml | 3 + app/src/main/res/values/ic_launcher_background.xml | 4 + app/src/main/res/values/strings.xml | 26 +++ app/src/main/res/values/styles.xml | 20 +++ app/src/main/res/xml/root_preferences.xml | 32 ++++ app/src/main/res/xml/wallpaper.xml | 6 + app/svgs/generate_video.sh | 7 + app/svgs/video_file_error.png | Bin 0 -> 10587 bytes app/svgs/video_file_error.svg | 92 +++++++++++ app/svgs/video_file_missing.png | Bin 0 -> 10640 bytes app/svgs/video_file_missing.svg | 44 +++++ app/svgs/videowall.svg | 82 +++++++++ 52 files changed, 1117 insertions(+) create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/proguard-rules.pro create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/ic_launcher-web.png create mode 100644 app/src/main/java/com/javispedro/wallmotion/MainActivity.java create mode 100644 app/src/main/java/com/javispedro/wallmotion/Renderer.java create mode 100644 app/src/main/java/com/javispedro/wallmotion/SettingsActivity.java create mode 100644 app/src/main/java/com/javispedro/wallmotion/WallService.java create mode 100644 app/src/main/res/drawable-anydpi/ic_action_settings.xml create mode 100644 app/src/main/res/drawable-anydpi/ic_action_wallpaper.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_action_settings.png create mode 100644 app/src/main/res/drawable-hdpi/ic_action_wallpaper.png create mode 100644 app/src/main/res/drawable-mdpi/ic_action_settings.png create mode 100644 app/src/main/res/drawable-mdpi/ic_action_wallpaper.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_action_settings.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_action_wallpaper.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_action_settings.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_action_wallpaper.png create mode 100644 app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 app/src/main/res/drawable/ic_logo.xml create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/layout/settings_activity.xml create mode 100644 app/src/main/res/menu/menu_main.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/raw/video_file_error.mp4 create mode 100644 app/src/main/res/raw/video_file_missing.mp4 create mode 100644 app/src/main/res/values/arrays.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/dimens.xml create mode 100644 app/src/main/res/values/ic_launcher_background.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 app/src/main/res/xml/root_preferences.xml create mode 100644 app/src/main/res/xml/wallpaper.xml create mode 100755 app/svgs/generate_video.sh create mode 100644 app/svgs/video_file_error.png create mode 100644 app/svgs/video_file_error.svg create mode 100644 app/svgs/video_file_missing.png create mode 100644 app/svgs/video_file_missing.svg create mode 100644 app/svgs/videowall.svg (limited to 'app') 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 Binary files /dev/null and b/app/src/main/ic_launcher-web.png 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 @@ + + + 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 @@ + + + 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 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_settings.png 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 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_wallpaper.png 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 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_settings.png 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 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_wallpaper.png 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 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_settings.png 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 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_wallpaper.png 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 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_settings.png 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 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_wallpaper.png 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 @@ + + + + + + + + + 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 @@ + + + + + + + + + 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 @@ + + + + + + + + + + + + + + \ 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 @@ + + + + \ 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 @@ + + + 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 @@ + + + + + \ 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 @@ + + + + + \ 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 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png 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 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png 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 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png 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 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png 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 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png 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 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png 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 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png 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 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png 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 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png 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 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png 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 Binary files /dev/null and b/app/src/main/res/raw/video_file_error.mp4 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 Binary files /dev/null and b/app/src/main/res/raw/video_file_missing.mp4 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 @@ + + + + Stretch to fit + Scale and crop to fit + + + @string/settings_display_scale_to_fit_key + @string/settings_display_scale_to_fit_with_cropping_key + + \ 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 @@ + + + #008577 + #00574B + #D81B60 + 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 @@ + + 16dp + 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 @@ + + + #A4A633 + \ 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 @@ + + Wallmotion + + Use a video file as wallpaper + + + Settings + + + Wallmotion settings + Source video + video_file + Video file + video_file_clear + Reset to (none) + (None) + Display options + scale + Scaling + scale_to_fit + scale_to_fit_with_cropping + restart + Restart video on each display + Video will be restarted every time wallpaper is shown + Video time will be remembered every time wallpaper is shown + 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 @@ + + + + + + + +