diff options
Diffstat (limited to 'app/src/main')
50 files changed, 1134 insertions, 0 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..d6fb563 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools"> + + <application + android:allowBackup="true" + android:dataExtractionRules="@xml/data_extraction_rules" + android:fullBackupContent="@xml/backup_rules" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:roundIcon="@mipmap/ic_launcher_round" + android:supportsRtl="true" + android:theme="@style/Theme.CleverSync" + tools:targetApi="31"> + <activity + android:name=".MainActivity" + android:exported="true" + android:theme="@style/Theme.CleverSync"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/app/src/main/java/org/the_jk/cleversync/FirstFragment.kt b/app/src/main/java/org/the_jk/cleversync/FirstFragment.kt new file mode 100644 index 0000000..fa73bf6 --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/FirstFragment.kt @@ -0,0 +1,35 @@ +package org.the_jk.cleversync + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.navigation.fragment.findNavController +import org.the_jk.cleversync.databinding.FragmentFirstBinding + +class FirstFragment : Fragment() { + private var _binding: FragmentFirstBinding? = null + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentFirstBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.buttonFirst.setOnClickListener { + findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment) + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} diff --git a/app/src/main/java/org/the_jk/cleversync/LiveDataUtils.kt b/app/src/main/java/org/the_jk/cleversync/LiveDataUtils.kt new file mode 100644 index 0000000..7f6ab1f --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/LiveDataUtils.kt @@ -0,0 +1,14 @@ +package org.the_jk.cleversync + +import androidx.lifecycle.LiveData +import androidx.lifecycle.Observer + +fun <T> LiveData<T>.safeValue(): T? { + if (this.hasActiveObservers()) + return value + var ret: T? = null + val observer = Observer<T> { value -> ret = value } + this.observeForever(observer) + this.removeObserver(observer) + return ret +} diff --git a/app/src/main/java/org/the_jk/cleversync/MainActivity.kt b/app/src/main/java/org/the_jk/cleversync/MainActivity.kt new file mode 100644 index 0000000..96fd167 --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/MainActivity.kt @@ -0,0 +1,60 @@ +package org.the_jk.cleversync + +import android.os.Bundle +import com.google.android.material.snackbar.Snackbar +import androidx.appcompat.app.AppCompatActivity +import androidx.navigation.findNavController +import androidx.navigation.ui.AppBarConfiguration +import androidx.navigation.ui.navigateUp +import androidx.navigation.ui.setupActionBarWithNavController +import android.view.Menu +import android.view.MenuItem +import org.the_jk.cleversync.databinding.ActivityMainBinding + +class MainActivity : AppCompatActivity() { + private lateinit var appBarConfiguration: AppBarConfiguration + private lateinit var binding: ActivityMainBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + setSupportActionBar(binding.toolbar) + + val navController = + findNavController(R.id.nav_host_fragment_content_main) + appBarConfiguration = AppBarConfiguration(navController.graph) + setupActionBarWithNavController(navController, appBarConfiguration) + + binding.fab.setOnClickListener { view -> + Snackbar.make( + view, + "Replace with your own action", + Snackbar.LENGTH_LONG + ) + .setAction("Action", null) + .setAnchorView(R.id.fab).show() + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_main, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.action_settings -> true + else -> super.onOptionsItemSelected(item) + } + } + + override fun onSupportNavigateUp(): Boolean { + val navController = + findNavController(R.id.nav_host_fragment_content_main) + return navController.navigateUp(appBarConfiguration) + || super.onSupportNavigateUp() + } +} diff --git a/app/src/main/java/org/the_jk/cleversync/SecondFragment.kt b/app/src/main/java/org/the_jk/cleversync/SecondFragment.kt new file mode 100644 index 0000000..b105450 --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/SecondFragment.kt @@ -0,0 +1,35 @@ +package org.the_jk.cleversync + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.navigation.fragment.findNavController +import org.the_jk.cleversync.databinding.FragmentSecondBinding + +class SecondFragment : Fragment() { + private var _binding: FragmentSecondBinding? = null + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentSecondBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.buttonSecond.setOnClickListener { + findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment) + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} diff --git a/app/src/main/java/org/the_jk/cleversync/io/Directory.kt b/app/src/main/java/org/the_jk/cleversync/io/Directory.kt new file mode 100644 index 0000000..2273f3e --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/io/Directory.kt @@ -0,0 +1,15 @@ +package org.the_jk.cleversync.io + +import androidx.lifecycle.LiveData + +interface Directory { + val name: String + + fun list(): Content + + data class Content( + val directories: LiveData<List<Directory>>, + val files: LiveData<List<File>>, + val links: LiveData<List<Link>>, + ) +} diff --git a/app/src/main/java/org/the_jk/cleversync/io/File.kt b/app/src/main/java/org/the_jk/cleversync/io/File.kt new file mode 100644 index 0000000..b5333eb --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/io/File.kt @@ -0,0 +1,12 @@ +package org.the_jk.cleversync.io + +import java.io.InputStream +import java.time.Instant + +interface File { + val name: String + val size: ULong + val lastModified: Instant + + fun open(): InputStream +} diff --git a/app/src/main/java/org/the_jk/cleversync/io/Link.kt b/app/src/main/java/org/the_jk/cleversync/io/Link.kt new file mode 100644 index 0000000..3ecf5e6 --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/io/Link.kt @@ -0,0 +1,13 @@ +package org.the_jk.cleversync.io + +interface Link { + val name: String + + fun resolve(): LinkTarget + + sealed class LinkTarget + + class DirectoryTarget(val directory: Directory): LinkTarget() + class FileTarget(val file: File): LinkTarget() + data object NoTarget: LinkTarget() +} diff --git a/app/src/main/java/org/the_jk/cleversync/io/ModifiableDirectory.kt b/app/src/main/java/org/the_jk/cleversync/io/ModifiableDirectory.kt new file mode 100644 index 0000000..43efa8f --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/io/ModifiableDirectory.kt @@ -0,0 +1,21 @@ +package org.the_jk.cleversync.io + +import androidx.lifecycle.LiveData + +interface ModifiableDirectory : Directory { + fun modifiableList(): Content + + fun createDirectory(name: String): ModifiableDirectory + fun createFile(name: String): ModifiableFile + fun createLink(name: String, target: String): ModifiableLink + + fun removeDirectory(name: String): Boolean + fun removeFile(name: String): Boolean + fun removeLink(name: String): Boolean + + data class Content( + val directories: LiveData<List<ModifiableDirectory>>, + val files: LiveData<List<ModifiableFile>>, + val links: LiveData<List<ModifiableLink>>, + ) +} diff --git a/app/src/main/java/org/the_jk/cleversync/io/ModifiableFile.kt b/app/src/main/java/org/the_jk/cleversync/io/ModifiableFile.kt new file mode 100644 index 0000000..8675dae --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/io/ModifiableFile.kt @@ -0,0 +1,7 @@ +package org.the_jk.cleversync.io + +import java.io.OutputStream + +interface ModifiableFile : File { + fun write(): OutputStream +} diff --git a/app/src/main/java/org/the_jk/cleversync/io/ModifiableLink.kt b/app/src/main/java/org/the_jk/cleversync/io/ModifiableLink.kt new file mode 100644 index 0000000..a20bb6a --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/io/ModifiableLink.kt @@ -0,0 +1,15 @@ +package org.the_jk.cleversync.io + +interface ModifiableLink : Link { + fun modifiableResolve(): ModifiableLinkTarget + + fun target(directory: Directory) = target(directory.name) + fun target(file: File) = target(file.name) + fun target(name: String) + + sealed class ModifiableLinkTarget + + class ModifiableDirectoryTarget(val directory: ModifiableDirectory): ModifiableLinkTarget() + class ModifiableFileTarget(val file: ModifiableFile): ModifiableLinkTarget() + data object ModifiableNoTarget: ModifiableLinkTarget() +} diff --git a/app/src/main/java/org/the_jk/cleversync/io/ModifiableTree.kt b/app/src/main/java/org/the_jk/cleversync/io/ModifiableTree.kt new file mode 100644 index 0000000..383360d --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/io/ModifiableTree.kt @@ -0,0 +1,3 @@ +package org.the_jk.cleversync.io + +interface ModifiableTree : Tree, ModifiableDirectory diff --git a/app/src/main/java/org/the_jk/cleversync/io/Tree.kt b/app/src/main/java/org/the_jk/cleversync/io/Tree.kt new file mode 100644 index 0000000..b6f2d54 --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/io/Tree.kt @@ -0,0 +1,7 @@ +package org.the_jk.cleversync.io + +import android.content.res.Resources + +interface Tree : Directory { + fun description(resources: Resources): CharSequence +} diff --git a/app/src/main/java/org/the_jk/cleversync/io/TreeFactory.kt b/app/src/main/java/org/the_jk/cleversync/io/TreeFactory.kt new file mode 100644 index 0000000..d7c22f5 --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/io/TreeFactory.kt @@ -0,0 +1,12 @@ +package org.the_jk.cleversync.io + +import org.the_jk.cleversync.io.impl.PathTree +import java.nio.file.Path + +object TreeFactory { + fun localModifiableTree(root: Path): ModifiableTree { + return PathTree(root) + } + + fun localTree(root: Path): Tree = localModifiableTree(root) +} diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathDirectory.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathDirectory.kt new file mode 100644 index 0000000..fab4dcc --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/io/impl/PathDirectory.kt @@ -0,0 +1,161 @@ +package org.the_jk.cleversync.io.impl + +import androidx.annotation.AnyThread +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.map +import org.the_jk.cleversync.io.Directory +import org.the_jk.cleversync.io.ModifiableDirectory +import org.the_jk.cleversync.io.ModifiableFile +import org.the_jk.cleversync.io.ModifiableLink +import java.nio.file.LinkOption +import java.nio.file.NoSuchFileException +import java.nio.file.Path +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.createDirectory +import kotlin.io.path.createSymbolicLinkPointingTo +import kotlin.io.path.deleteIfExists +import kotlin.io.path.deleteRecursively +import kotlin.io.path.isDirectory +import kotlin.io.path.isRegularFile +import kotlin.io.path.isSymbolicLink +import kotlin.io.path.listDirectoryEntries +import kotlin.io.path.name + +@OptIn(ExperimentalPathApi::class) +internal open class PathDirectory( + private val path: Path, + private val pathWatcher: PathWatcher, +) : ModifiableDirectory { + private val watcher: DirectoryWatcher by lazy { + DirectoryWatcher() + } + + private val modifiableContent: ModifiableDirectory.Content by lazy { + val base = watcher.content + ModifiableDirectory.Content( + base.map { entries -> + entries.filterIsInstance<Entry.Directory>().map { entry -> + PathDirectory(entry.path, pathWatcher) + } + }, + base.map { entries -> + entries.filterIsInstance<Entry.File>().map { entry -> + PathFile(entry.path) + } + }, + base.map { entries -> + entries.filterIsInstance<Entry.Link>().map { entry -> + PathLink(entry.path, pathWatcher) + } + }, + ) + } + + private val content: Directory.Content by lazy { + val base = modifiableContent + Directory.Content( + base.directories.map { it }, + base.files.map { it }, + base.links.map { it }, + ) + } + + override fun modifiableList() = modifiableContent + + override fun createDirectory(name: String): ModifiableDirectory { + val path = path.resolve(name) + return PathDirectory(path.createDirectory(), pathWatcher) + } + + override fun createFile(name: String): ModifiableFile { + val path = path.resolve(name) + return PathFile(path) + } + + override fun createLink(name: String, target: String): ModifiableLink { + val path = path.resolve(name) + return PathLink(path.createSymbolicLinkPointingTo(path.resolve(target)), pathWatcher) + } + + override fun removeDirectory(name: String): Boolean { + val path = path.resolve(name) + return if (path.isDirectory(LinkOption.NOFOLLOW_LINKS)) { + path.deleteRecursively() + true + } else false + } + + override fun removeFile(name: String): Boolean { + val path = path.resolve(name) + return path.isRegularFile(LinkOption.NOFOLLOW_LINKS) && path.deleteIfExists() + } + + override fun removeLink(name: String): Boolean { + val path = path.resolve(name) + return path.isSymbolicLink() && path.deleteIfExists() + } + + override val name: String + get() = path.name + + override fun list() = content + + override fun equals(other: Any?) = other is PathDirectory && other.path == path + override fun hashCode() = path.hashCode() + + private sealed class Entry(val path: Path) { + class Directory(path: Path) : Entry(path) + class File(path: Path) : Entry(path) + class Link(path: Path) : Entry(path) + } + + private inner class DirectoryWatcher : PathWatcher.Delegate { + val content: LiveData<List<Entry>> + get() = _content + + @AnyThread + override fun update(added: List<Path>, removed: List<Path>) { + try { + _content.postValue(mapEntries(path.listDirectoryEntries())) + } catch (ignored: NoSuchFileException) { + } + } + + private val _content = object : MutableLiveData<List<Entry>>() { + override fun onActive() { + setup() + } + + override fun onInactive() { + clear() + } + } + + private fun setup() { + val entries = path.listDirectoryEntries() + pathWatcher.add(path, this) + _content.value = mapEntries(entries) + } + + private fun clear() { + pathWatcher.remove(path) + } + } + + companion object { + private fun mapEntries(entries: List<Path>): List<Entry> { + return entries.mapNotNull { + if (it.isDirectory(LinkOption.NOFOLLOW_LINKS)) { + Entry.Directory(it) + } else if (it.isRegularFile(LinkOption.NOFOLLOW_LINKS)) { + Entry.File(it) + } else if (it.isSymbolicLink()) { + Entry.Link(it) + } else { + null + } + } + } + } +} diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathFile.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathFile.kt new file mode 100644 index 0000000..d8ca900 --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/io/impl/PathFile.kt @@ -0,0 +1,65 @@ +package org.the_jk.cleversync.io.impl + +import org.the_jk.cleversync.io.ModifiableFile +import java.io.InputStream +import java.io.OutputStream +import java.nio.file.Files +import java.nio.file.LinkOption +import java.nio.file.Path +import java.nio.file.StandardCopyOption +import java.nio.file.StandardOpenOption +import java.time.Instant +import kotlin.io.path.exists +import kotlin.io.path.fileSize +import kotlin.io.path.getLastModifiedTime +import kotlin.io.path.inputStream +import kotlin.io.path.name +import kotlin.io.path.outputStream + +internal class PathFile(private val path: Path) : ModifiableFile { + override fun write(): OutputStream { + // If file doesn't exist, write to it directly. + if (!path.exists(LinkOption.NOFOLLOW_LINKS)) + return path.outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) + + // Otherwise, write to temp file, only overwriting when done. + val tmp = path.parent.resolve(".#" + path.name) + val os = tmp.outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) + return object : OutputStream() { + override fun write(value: Int) { + os.write(value) + } + + override fun write(b: ByteArray) { + os.write(b) + } + + override fun write(b: ByteArray, off: Int, len: Int) { + os.write(b, off, len) + } + + override fun flush() { + os.flush() + } + + override fun close() { + os.close() + Files.move(tmp, path, StandardCopyOption.ATOMIC_MOVE) + } + } + } + + override val name: String + get() = path.name + override val size: ULong + get() = path.fileSize().toULong() + override val lastModified: Instant + get() = path.getLastModifiedTime().toInstant() + + override fun open(): InputStream { + return path.inputStream(StandardOpenOption.READ) + } + + override fun equals(other: Any?) = other is PathFile && other.path == path + override fun hashCode() = path.hashCode() +} diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathLink.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathLink.kt new file mode 100644 index 0000000..5d11228 --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/io/impl/PathLink.kt @@ -0,0 +1,49 @@ +package org.the_jk.cleversync.io.impl + +import org.the_jk.cleversync.io.Link +import org.the_jk.cleversync.io.ModifiableLink +import java.nio.file.Path +import kotlin.io.path.createSymbolicLinkPointingTo +import kotlin.io.path.deleteIfExists +import kotlin.io.path.isDirectory +import kotlin.io.path.isRegularFile +import kotlin.io.path.name +import kotlin.io.path.readSymbolicLink + +internal class PathLink( + private val path: Path, + private val pathWatcher: PathWatcher, +) : ModifiableLink { + override fun modifiableResolve(): ModifiableLink.ModifiableLinkTarget { + val target = path.readSymbolicLink() + return if (target.isDirectory()) { + ModifiableLink.ModifiableDirectoryTarget(PathDirectory(target, pathWatcher)) + } else if (target.isRegularFile()) { + ModifiableLink.ModifiableFileTarget(PathFile(target)) + } else { + ModifiableLink.ModifiableNoTarget + } + } + + override fun target(name: String) { + path.deleteIfExists() + path.createSymbolicLinkPointingTo(path.resolve(name)) + } + + override val name: String + get() = path.name + + override fun equals(other: Any?) = other is PathLink && other.path == path + override fun hashCode() = path.hashCode() + + override fun resolve(): Link.LinkTarget { + val target = path.readSymbolicLink() + return if (target.isDirectory()) { + Link.DirectoryTarget(PathDirectory(target, pathWatcher)) + } else if (target.isRegularFile()) { + Link.FileTarget(PathFile(target)) + } else { + Link.NoTarget + } + } +} diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathTree.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathTree.kt new file mode 100644 index 0000000..a8a74c5 --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/io/impl/PathTree.kt @@ -0,0 +1,10 @@ +package org.the_jk.cleversync.io.impl + +import android.content.res.Resources +import org.the_jk.cleversync.R +import org.the_jk.cleversync.io.ModifiableTree +import java.nio.file.Path + +internal class PathTree(root: Path) : PathDirectory(root, PathWatcher()), ModifiableTree { + override fun description(resources: Resources) = resources.getString(R.string.local_directory) +} diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathWatcher.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathWatcher.kt new file mode 100644 index 0000000..945019a --- /dev/null +++ b/app/src/main/java/org/the_jk/cleversync/io/impl/PathWatcher.kt @@ -0,0 +1,82 @@ +package org.the_jk.cleversync.io.impl + +import androidx.annotation.GuardedBy +import androidx.annotation.WorkerThread +import java.nio.file.ClosedWatchServiceException +import java.nio.file.FileSystems +import java.nio.file.Path +import java.nio.file.StandardWatchEventKinds +import java.nio.file.WatchKey +import java.nio.file.WatchService +import java.util.concurrent.Executors + +internal class PathWatcher { + private val executor = Executors.newSingleThreadExecutor() + private var service: WatchService? = null + private val keys = mutableMapOf<Path, WatchKey>() + private val lock = Object() + @GuardedBy("lock") + private val delegates = mutableMapOf<WatchKey, Delegate>() + + fun add(path: Path, delegate: Delegate) { + if (keys.isEmpty()) { + val service = FileSystems.getDefault().newWatchService() + executor.execute{ runner(service) } + this.service = service + } + val key = path.register(service!!, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE) + delegates[key] = delegate + keys[path] = key + } + + fun remove(path: Path) { + val key = keys.remove(path) ?: return + key.cancel() + synchronized(lock) { + delegates.remove(key) + } + if (keys.isEmpty()) { + service?.close() + service = null + } + } + + interface Delegate { + fun update(added: List<Path>, removed: List<Path>) + } + + @WorkerThread + private fun runner(service: WatchService) { + while (true) { + val key: WatchKey + try { + key = service.take() + } catch (ignored: InterruptedException) { + return + } catch (ignored: ClosedWatchServiceException) { + return + } + + val added = mutableListOf<Path>() + val removed = mutableListOf<Path>() + var overflow = false + + for (event in key.pollEvents()) { + when (event.kind()) { + StandardWatchEventKinds.OVERFLOW -> overflow = true + StandardWatchEventKinds.ENTRY_CREATE -> added.add(event.context() as Path) + StandardWatchEventKinds.ENTRY_DELETE -> removed.add(event.context() as Path) + } + } + + if (overflow || added.isNotEmpty() || removed.isNotEmpty()) { + val delegate = synchronized(lock) { + delegates[key] + } + delegate?.update(added, removed) + } + + key.reset() + } + } +} diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..1e4408c --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportHeight="108" + android:viewportWidth="108"> + <path + android:fillColor="#3DDC84" + android:pathData="M0,0h108v108h-108z" /> + <path + android:fillColor="#00000000" + android:pathData="M9,0L9,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,0L19,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M29,0L29,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M39,0L39,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M49,0L49,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M59,0L59,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M69,0L69,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M79,0L79,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M89,0L89,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M99,0L99,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,9L108,9" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,19L108,19" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,29L108,29" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,39L108,39" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,49L108,49" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,59L108,59" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,69L108,69" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,79L108,79" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,89L108,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,99L108,99" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,29L89,29" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,39L89,39" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,49L89,49" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,59L89,59" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,69L89,69" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,79L89,79" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M29,19L29,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M39,19L39,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M49,19L49,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M59,19L59,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M69,19L69,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M79,19L79,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> +</vector> 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..14780bb --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="108dp" + android:height="108dp" + android:viewportHeight="108" + android:viewportWidth="108"> + <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="85.84757" + android:endY="92.4963" + android:startX="42.9492" + android:startY="49.59793" + android:type="linear"> + <item + android:color="#44000000" + android:offset="0.0" /> + <item + android:color="#00000000" + android:offset="1.0" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillColor="#FFFFFF" + android:fillType="nonZero" + android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" + android:strokeColor="#00000000" + android:strokeWidth="1" /> +</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..b214f0f --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,33 @@ +<?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" + android:fitsSystemWindows="true" + tools:context=".MainActivity"> + + <com.google.android.material.appbar.AppBarLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:fitsSystemWindows="true"> + + <com.google.android.material.appbar.MaterialToolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" /> + + </com.google.android.material.appbar.AppBarLayout> + + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/fab" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|end" + android:layout_marginBottom="16dp" + android:layout_marginEnd="@dimen/fab_margin" + app:srcCompat="@android:drawable/ic_dialog_email" /> + + <include layout="@layout/content_main" /> + +</androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml new file mode 100644 index 0000000..041049e --- /dev/null +++ b/app/src/main/res/layout/content_main.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + + <fragment + android:id="@+id/nav_host_fragment_content_main" + android:name="androidx.navigation.fragment.NavHostFragment" + android:layout_width="0dp" + android:layout_height="0dp" + app:defaultNavHost="true" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:navGraph="@navigation/nav_graph" /> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/fragment_first.xml b/app/src/main/res/layout/fragment_first.xml new file mode 100644 index 0000000..a3a474c --- /dev/null +++ b/app/src/main/res/layout/fragment_first.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.core.widget.NestedScrollView 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=".FirstFragment"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="16dp"> + + <Button + android:id="@+id/button_first" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/next" + app:layout_constraintBottom_toTopOf="@id/textview_first" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/textview_first" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:text="@string/lorem_ipsum" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/button_first" /> + </androidx.constraintlayout.widget.ConstraintLayout> +</androidx.core.widget.NestedScrollView> diff --git a/app/src/main/res/layout/fragment_second.xml b/app/src/main/res/layout/fragment_second.xml new file mode 100644 index 0000000..cc64afc --- /dev/null +++ b/app/src/main/res/layout/fragment_second.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.core.widget.NestedScrollView 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=".SecondFragment"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="16dp"> + + <Button + android:id="@+id/button_second" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/previous" + app:layout_constraintBottom_toTopOf="@id/textview_second" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/textview_second" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:text="@string/lorem_ipsum" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/button_second" /> + </androidx.constraintlayout.widget.ConstraintLayout> +</androidx.core.widget.NestedScrollView> 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..6aac919 --- /dev/null +++ b/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,10 @@ +<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="org.the_jk.cleversync.MainActivity"> + <item + android:id="@+id/action_settings" + android:orderInCategory="100" + android:title="@string/action_settings" + app:showAsAction="never" /> +</menu> diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..b3e26b4 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/ic_launcher_background" /> + <foreground android:drawable="@drawable/ic_launcher_foreground" /> + <monochrome android:drawable="@drawable/ic_launcher_foreground" /> +</adaptive-icon> diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..b3e26b4 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/ic_launcher_background" /> + <foreground android:drawable="@drawable/ic_launcher_foreground" /> + <monochrome android:drawable="@drawable/ic_launcher_foreground" /> +</adaptive-icon> diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000..c209e78 --- /dev/null +++ b/app/src/main/res/mipmap-hdpi/ic_launcher.webp diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000..b2dfe3d --- /dev/null +++ b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000..4f0f1d6 --- /dev/null +++ b/app/src/main/res/mipmap-mdpi/ic_launcher.webp diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000..62b611d --- /dev/null +++ b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000..948a307 --- /dev/null +++ b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000..1b9a695 --- /dev/null +++ b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000..28d4b77 --- /dev/null +++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000..9287f50 --- /dev/null +++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000..aa7d642 --- /dev/null +++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000..9126ae3 --- /dev/null +++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml new file mode 100644 index 0000000..9c92ead --- /dev/null +++ b/app/src/main/res/navigation/nav_graph.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<navigation 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:id="@+id/nav_graph" + app:startDestination="@id/FirstFragment"> + + <fragment + android:id="@+id/FirstFragment" + android:name="org.the_jk.cleversync.FirstFragment" + android:label="@string/first_fragment_label" + tools:layout="@layout/fragment_first"> + + <action + android:id="@+id/action_FirstFragment_to_SecondFragment" + app:destination="@id/SecondFragment" /> + </fragment> + <fragment + android:id="@+id/SecondFragment" + android:name="org.the_jk.cleversync.SecondFragment" + android:label="@string/second_fragment_label" + tools:layout="@layout/fragment_second"> + + <action + android:id="@+id/action_SecondFragment_to_FirstFragment" + app:destination="@id/FirstFragment" /> + </fragment> +</navigation> diff --git a/app/src/main/res/values-land/dimens.xml b/app/src/main/res/values-land/dimens.xml new file mode 100644 index 0000000..ec4deb8 --- /dev/null +++ b/app/src/main/res/values-land/dimens.xml @@ -0,0 +1,3 @@ +<resources> + <dimen name="fab_margin">48dp</dimen> +</resources> diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..e6c567a --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,7 @@ +<resources xmlns:tools="http://schemas.android.com/tools"> + <!-- Base application theme. --> + <style name="Base.Theme.CleverSync" parent="Theme.Material3.DayNight.NoActionBar"> + <!-- Customize your dark theme here. --> + <!-- <item name="colorPrimary">@color/my_dark_primary</item> --> + </style> +</resources> diff --git a/app/src/main/res/values-v23/themes.xml b/app/src/main/res/values-v23/themes.xml new file mode 100644 index 0000000..5d974d6 --- /dev/null +++ b/app/src/main/res/values-v23/themes.xml @@ -0,0 +1,10 @@ +<resources xmlns:tools="http://schemas.android.com/tools"> + + <style name="Theme.CleverSync" parent="Base.Theme.CleverSync"> + <!-- Transparent system bars for edge-to-edge. --> + <item name="android:navigationBarColor">@android:color/transparent + </item> + <item name="android:statusBarColor">@android:color/transparent</item> + <item name="android:windowLightStatusBar">?attr/isLightTheme</item> + </style> +</resources> diff --git a/app/src/main/res/values-w1240dp/dimens.xml b/app/src/main/res/values-w1240dp/dimens.xml new file mode 100644 index 0000000..2ecead2 --- /dev/null +++ b/app/src/main/res/values-w1240dp/dimens.xml @@ -0,0 +1,3 @@ +<resources> + <dimen name="fab_margin">200dp</dimen> +</resources> diff --git a/app/src/main/res/values-w600dp/dimens.xml b/app/src/main/res/values-w600dp/dimens.xml new file mode 100644 index 0000000..ec4deb8 --- /dev/null +++ b/app/src/main/res/values-w600dp/dimens.xml @@ -0,0 +1,3 @@ +<resources> + <dimen name="fab_margin">48dp</dimen> +</resources> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..768b058 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="black">#FF000000</color> + <color name="white">#FFFFFFFF</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/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..723c6e1 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,55 @@ +<resources> + <string name="app_name">CleverSync</string> + <string name="action_settings">Settings</string> + <!-- Strings used for fragments for navigation --> + <string name="first_fragment_label">First Fragment</string> + <string name="second_fragment_label">Second Fragment</string> + <string name="next">Next</string> + <string name="previous">Previous</string> + + <string name="lorem_ipsum"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in + scelerisque sem. Mauris volutpat, dolor id interdum ullamcorper, risus + dolor egestas lectus, sit amet mattis purus dui nec risus. Maecenas non + sodales nisi, vel dictum dolor. Class aptent taciti sociosqu ad litora + torquent per conubia nostra, per inceptos himenaeos. Suspendisse blandit + eleifend diam, vel rutrum tellus vulputate quis. Aliquam eget libero + aliquet, imperdiet nisl a, ornare ex. Sed rhoncus est ut libero porta + lobortis. Fusce in dictum tellus.\n\n + Suspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id + magna felis. Vivamus egestas, est a condimentum egestas, turpis nisl + iaculis ipsum, in dictum tellus dolor sed neque. Morbi tellus erat, + dapibus ut sem a, iaculis tincidunt dui. Interdum et malesuada fames ac + ante ipsum primis in faucibus. Curabitur et eros porttitor, ultricies + urna vitae, molestie nibh. Phasellus at commodo eros, non aliquet metus. + Sed maximus nisl nec dolor bibendum, vel congue leo egestas.\n\n + Sed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi + odio, condimentum sit amet auctor at, mollis non turpis. Nullam pretium + libero vestibulum, finibus orci vel, molestie quam. Fusce blandit + tincidunt nulla, quis sollicitudin libero facilisis et. Integer interdum + nunc ligula, et fermentum metus hendrerit id. Vestibulum lectus felis, + dictum at lacinia sit amet, tristique id quam. Cras eu consequat dui. + Suspendisse sodales nunc ligula, in lobortis sem porta sed. Integer id + ultrices magna, in luctus elit. Sed a pellentesque est.\n\n + Aenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam + a venenatis nibh. Morbi laoreet, tortor sed facilisis varius, nibh orci + rhoncus nulla, id elementum leo dui non lorem. Nam mollis ipsum quis + auctor varius. Quisque elementum eu libero sed commodo. In eros nisl, + imperdiet vel imperdiet et, scelerisque a mauris. Pellentesque varius ex + nunc, quis imperdiet eros placerat ac. Duis finibus orci et est auctor + tincidunt. Sed non viverra ipsum. Nunc quis augue egestas, cursus lorem + at, molestie sem. Morbi a consectetur ipsum, a placerat diam. Etiam + vulputate dignissim convallis. Integer faucibus mauris sit amet finibus + convallis.\n\n + Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus + et netus et malesuada fames ac turpis egestas. In volutpat arcu ut felis + sagittis, in finibus massa gravida. Pellentesque id tellus orci. Integer + dictum, lorem sed efficitur ullamcorper, libero justo consectetur ipsum, + in mollis nisl ex sed nisl. Donec maximus ullamcorper sodales. Praesent + bibendum rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus + libero vel nunc consequat, quis tincidunt nisl eleifend. Cras bibendum + enim a justo luctus vestibulum. Fusce dictum libero quis erat maximus, + vitae volutpat diam dignissim. + </string> + <string name="local_directory">Local directory</string> +</resources> diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..6c0b175 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,9 @@ +<resources xmlns:tools="http://schemas.android.com/tools"> + <!-- Base application theme. --> + <style name="Base.Theme.CleverSync" parent="Theme.Material3.DayNight.NoActionBar"> + <!-- Customize your light theme here. --> + <!-- <item name="colorPrimary">@color/my_light_primary</item> --> + </style> + + <style name="Theme.CleverSync" parent="Base.Theme.CleverSync" /> +</resources> diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..10d94d3 --- /dev/null +++ b/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<full-backup-content> +<!-- + <include domain="sharedpref" path="."/> + <exclude domain="sharedpref" path="device.xml"/> +--> +</full-backup-content> diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..4f7fc1d --- /dev/null +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<data-extraction-rules> + <cloud-backup> + <!-- TODO: Use <include> and <exclude> to control what is backed up. + <include .../> + <exclude .../> + --> + </cloud-backup> + <device-transfer> + <!-- + <include .../> + <exclude .../> + --> + </device-transfer> +</data-extraction-rules> |
