diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteMediaNotificationProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteMediaNotificationProvider.kt index 8386958cc9..8b5e7aeaa0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteMediaNotificationProvider.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteMediaNotificationProvider.kt @@ -18,7 +18,6 @@ import androidx.media3.common.MediaMetadata import androidx.media3.common.Player import androidx.media3.common.util.Assertions import androidx.media3.common.util.UnstableApi -import androidx.media3.common.util.Util import androidx.media3.session.CommandButton import androidx.media3.session.DefaultMediaNotificationProvider import androidx.media3.session.MediaNotification diff --git a/dependencies.gradle b/dependencies.gradle index f75271ef0a..8863ca5353 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -29,6 +29,7 @@ dependencyResolutionManagement { library('androidx-compose-material3', 'androidx.compose.material3', 'material3').withoutVersion() library('androidx-compose-ui-tooling-preview', 'androidx.compose.ui', 'ui-tooling-preview').withoutVersion() library('androidx-compose-ui-tooling-core', 'androidx.compose.ui', 'ui-tooling').withoutVersion() + library('androidx-compose-ui-test-manifest', 'androidx.compose.ui', 'ui-test-manifest').withoutVersion() library('androidx-compose-runtime-livedata', 'androidx.compose.runtime', 'runtime-livedata').withoutVersion() library('androidx-compose-rxjava3', 'androidx.compose.runtime:runtime-rxjava3:1.4.2') library('ktlint-twitter-compose', 'com.twitter.compose.rules:ktlint:0.0.26') @@ -47,6 +48,7 @@ dependencyResolutionManagement { // Android X library('androidx-activity-ktx', 'androidx.activity', 'activity-ktx').versionRef('androidx-activity') + library('androidx-activity-compose', 'androidx.activity', 'activity-compose').versionRef('androidx-activity') library('androidx-appcompat', 'androidx.appcompat', 'appcompat').versionRef('androidx-appcompat') library('androidx-core-ktx', 'androidx.core:core-ktx:1.10.0') library('androidx-fragment-ktx', 'androidx.fragment', 'fragment-ktx').versionRef('androidx-fragment') diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index a5d056b1ab..6ef566d045 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -362,6 +362,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -428,6 +436,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + @@ -476,6 +489,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + @@ -489,6 +507,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + @@ -528,6 +551,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -1278,6 +1309,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + diff --git a/settings.gradle b/settings.gradle index 9ce38b14af..3040346ef7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -55,6 +55,8 @@ include ':sticky-header-grid' include ':photoview' include ':core-ui' include ':benchmark' +include ':microbenchmark' +include ':video-app' project(':app').name = 'Signal-Android' project(':paging').projectDir = file('paging/lib') @@ -86,4 +88,3 @@ project(':qr-app').projectDir = file('qr/app') rootProject.name='Signal' apply from: 'dependencies.gradle' -include ':microbenchmark' diff --git a/video-app/.gitignore b/video-app/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/video-app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/video-app/build.gradle.kts b/video-app/build.gradle.kts new file mode 100644 index 0000000000..fc01414921 --- /dev/null +++ b/video-app/build.gradle.kts @@ -0,0 +1,67 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +plugins { + id("signal-sample-app") +} + +val signalBuildToolsVersion: String by extra +val signalCompileSdkVersion: String by extra +val signalTargetSdkVersion: Int by extra +val signalMinSdkVersion: Int by extra +val signalJavaVersion: JavaVersion by extra + +android { + namespace = "org.thoughtcrime.video.app" + compileSdkVersion = signalCompileSdkVersion + + defaultConfig { + applicationId = "org.thoughtcrime.video.app" + minSdk = signalMinSdkVersion + targetSdk = signalTargetSdkVersion + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary = true + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + compileOptions { + sourceCompatibility = signalJavaVersion + targetCompatibility = signalJavaVersion + } + kotlinOptions { + jvmTarget = "11" + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.4.3" + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } +} + +dependencies { + implementation(libs.androidx.fragment.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.compose.material3) + implementation(libs.bundles.media3) + debugImplementation(libs.androidx.compose.ui.tooling.core) + debugImplementation(libs.androidx.compose.ui.test.manifest) +} diff --git a/video-app/proguard-rules.pro b/video-app/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/video-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 \ No newline at end of file diff --git a/video-app/src/androidTest/java/org/thoughtcrime/video/app/ExampleInstrumentedTest.kt b/video-app/src/androidTest/java/org/thoughtcrime/video/app/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..be3f148b04 --- /dev/null +++ b/video-app/src/androidTest/java/org/thoughtcrime/video/app/ExampleInstrumentedTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.video.app + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.thoughtcrime.video.app", appContext.packageName) + } +} diff --git a/video-app/src/main/AndroidManifest.xml b/video-app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..ad55630ca3 --- /dev/null +++ b/video-app/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/video-app/src/main/java/org/thoughtcrime/video/app/MainActivity.kt b/video-app/src/main/java/org/thoughtcrime/video/app/MainActivity.kt new file mode 100644 index 0000000000..267299b1f7 --- /dev/null +++ b/video-app/src/main/java/org/thoughtcrime/video/app/MainActivity.kt @@ -0,0 +1,118 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.video.app + +import android.os.Bundle +import android.util.Log +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.result.PickVisualMediaRequest +import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.viewModels +import androidx.annotation.OptIn +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.viewinterop.AndroidView +import androidx.media3.common.util.UnstableApi +import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.exoplayer.source.MediaSource +import androidx.media3.ui.PlayerView +import org.thoughtcrime.video.app.ui.theme.SignalTheme + +/** + * Main activity for this sample app. + */ +class MainActivity : ComponentActivity() { + private val viewModel: MainScreenViewModel by viewModels() + private lateinit var exoPlayer: ExoPlayer + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + viewModel.initialize(this) + exoPlayer = ExoPlayer.Builder(this).build() + setContent { + SignalTheme { + Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) { + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + val videoUri = viewModel.selectedVideo + if (videoUri == null) { + LabeledButton("Select Video") { pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly)) } + } else { + LabeledButton("Play Video") { viewModel.updateMediaSource(this@MainActivity) } + LabeledButton("Play Video with slow download") { viewModel.updateMediaSourceTrickle(this@MainActivity) } + ExoVideoView(source = viewModel.mediaSource, exoPlayer = exoPlayer) + } + } + } + } + } + } + + override fun onPause() { + super.onPause() + exoPlayer.pause() + } + + override fun onDestroy() { + super.onDestroy() + viewModel.releaseCache() + exoPlayer.stop() + exoPlayer.release() + } + + /** + * This launches the system media picker and stores the resulting URI. + */ + private val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri -> + if (uri != null) { + Log.d("PhotoPicker", "Selected URI: $uri") + viewModel.selectedVideo = uri + viewModel.updateMediaSource(this) + } else { + Log.d("PhotoPicker", "No media selected") + } + } +} + +@Composable +fun LabeledButton(buttonLabel: String, modifier: Modifier = Modifier, onClick: () -> Unit) { + Button(onClick = onClick, modifier = modifier) { + Text(buttonLabel) + } +} + +@OptIn(UnstableApi::class) +@Composable +fun ExoVideoView(source: MediaSource, exoPlayer: ExoPlayer, modifier: Modifier = Modifier) { + exoPlayer.playWhenReady = false + exoPlayer.setMediaSource(source) + exoPlayer.prepare() + AndroidView(factory = { context -> + PlayerView(context).apply { + player = exoPlayer + } + }, modifier = modifier) +} + +@Preview(showBackground = true) +@Composable +fun GreetingPreview() { + SignalTheme { + LabeledButton("Preview Render") {} + } +} diff --git a/video-app/src/main/java/org/thoughtcrime/video/app/MainScreenViewModel.kt b/video-app/src/main/java/org/thoughtcrime/video/app/MainScreenViewModel.kt new file mode 100644 index 0000000000..a91d6590e3 --- /dev/null +++ b/video-app/src/main/java/org/thoughtcrime/video/app/MainScreenViewModel.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.video.app + +import android.content.Context +import android.net.Uri +import androidx.annotation.OptIn +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.media3.common.MediaItem +import androidx.media3.common.util.UnstableApi +import androidx.media3.datasource.DefaultDataSource +import androidx.media3.datasource.cache.Cache +import androidx.media3.datasource.cache.CacheDataSource +import androidx.media3.datasource.cache.NoOpCacheEvictor +import androidx.media3.datasource.cache.SimpleCache +import androidx.media3.exoplayer.source.MediaSource +import androidx.media3.exoplayer.source.ProgressiveMediaSource +import androidx.media3.exoplayer.source.SilenceMediaSource +import java.io.File + +/** + * Main screen view model for the video sample app. + */ +@OptIn(UnstableApi::class) +class MainScreenViewModel : ViewModel() { + // Initialize an silent media source before the user selects a video. This is the closest I could find to an "empty" media source while still being nullsafe. + private val value by lazy { + val factory = SilenceMediaSource.Factory() + factory.setDurationUs(1000) + factory.createMediaSource() + } + + private lateinit var cache: Cache + + var selectedVideo: Uri? by mutableStateOf(null) + var mediaSource: MediaSource by mutableStateOf(value) + private set + + /** + * Initialize the backing cache. This is a file in the app's cache directory that has a random suffix to ensure you get cache misses on a new app launch. + * + * @param context required to get the file path of the cache directory. + */ + fun initialize(context: Context) { + val cacheDir = File(context.cacheDir.absolutePath) + cache = SimpleCache(File(cacheDir, getRandomString(12)), NoOpCacheEvictor()) + } + + fun updateMediaSource(context: Context) { + selectedVideo?.let { + mediaSource = ProgressiveMediaSource.Factory(DefaultDataSource.Factory(context)).createMediaSource(MediaItem.fromUri(it)) + } + } + + /** + * Replaces the media source with one that has a latency to each read from the media source, simulating network latency. + * It stores the result in a cache (that does not have a penalty) to better mimic real-world performance: + * once a chunk is downloaded from the network, it will not have to be re-fetched. + * + * @param context + */ + fun updateMediaSourceTrickle(context: Context) { + selectedVideo?.let { + val cacheFactory = CacheDataSource.Factory() + .setCache(cache) + .setUpstreamDataSourceFactory(SlowDataSource.Factory(context, 10)) + mediaSource = ProgressiveMediaSource.Factory(cacheFactory).createMediaSource(MediaItem.fromUri(it)) + } + } + + fun releaseCache() { + cache.release() + } + + /** + * Get random string. Will always return at least one character. + * + * @param length length of the returned string. + * @return a string composed of random alphanumeric characters of the specified length (minimum of 1). + */ + private fun getRandomString(length: Int): String { + val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9') + return (1..length.coerceAtLeast(1)) + .map { allowedChars.random() } + .joinToString("") + } +} diff --git a/video-app/src/main/java/org/thoughtcrime/video/app/SlowDataSource.kt b/video-app/src/main/java/org/thoughtcrime/video/app/SlowDataSource.kt new file mode 100644 index 0000000000..5406e1e156 --- /dev/null +++ b/video-app/src/main/java/org/thoughtcrime/video/app/SlowDataSource.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.video.app + +import android.content.Context +import android.net.Uri +import androidx.annotation.OptIn +import androidx.media3.common.util.UnstableApi +import androidx.media3.datasource.DataSource +import androidx.media3.datasource.DataSpec +import androidx.media3.datasource.DefaultDataSource +import androidx.media3.datasource.TransferListener + +/** + * This wraps a [DefaultDataSource] and adds [latency] to each read. This is intended to approximate a slow/shoddy network connection that drip-feeds in data. + * + * @property latency the amount, in milliseconds, that each read should be delayed. A good proxy for network ping. + * @constructor + * + * @param context used to initialize the underlying [DefaultDataSource.Factory] + */ +@OptIn(UnstableApi::class) +class SlowDataSource(context: Context, private val latency: Long) : DataSource { + private val internalDataSource: DataSource = DefaultDataSource.Factory(context).createDataSource() + + override fun read(buffer: ByteArray, offset: Int, length: Int): Int { + Thread.sleep(latency) + return internalDataSource.read(buffer, offset, length) + } + + override fun addTransferListener(transferListener: TransferListener) { + internalDataSource.addTransferListener(transferListener) + } + + override fun open(dataSpec: DataSpec): Long { + return internalDataSource.open(dataSpec) + } + + override fun getUri(): Uri? { + return internalDataSource.uri + } + + override fun close() { + return internalDataSource.close() + } + + class Factory(private val context: Context, private val latency: Long) : DataSource.Factory { + override fun createDataSource(): DataSource { + return SlowDataSource(context, latency) + } + } +} diff --git a/video-app/src/main/java/org/thoughtcrime/video/app/ui/theme/Color.kt b/video-app/src/main/java/org/thoughtcrime/video/app/ui/theme/Color.kt new file mode 100644 index 0000000000..90fc2bd952 --- /dev/null +++ b/video-app/src/main/java/org/thoughtcrime/video/app/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package org.thoughtcrime.video.app.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) diff --git a/video-app/src/main/java/org/thoughtcrime/video/app/ui/theme/Theme.kt b/video-app/src/main/java/org/thoughtcrime/video/app/ui/theme/Theme.kt new file mode 100644 index 0000000000..10c826c424 --- /dev/null +++ b/video-app/src/main/java/org/thoughtcrime/video/app/ui/theme/Theme.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.video.app.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun SignalTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + window.statusBarColor = colorScheme.primary.toArgb() + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme + } + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} diff --git a/video-app/src/main/java/org/thoughtcrime/video/app/ui/theme/Type.kt b/video-app/src/main/java/org/thoughtcrime/video/app/ui/theme/Type.kt new file mode 100644 index 0000000000..726f149a58 --- /dev/null +++ b/video-app/src/main/java/org/thoughtcrime/video/app/ui/theme/Type.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.video.app.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) diff --git a/video-app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/video-app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000000..743651fa54 --- /dev/null +++ b/video-app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/video-app/src/main/res/drawable/ic_launcher_background.xml b/video-app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000..126c9713e0 --- /dev/null +++ b/video-app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/video-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/video-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..80e53a5e47 --- /dev/null +++ b/video-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/video-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/video-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000..80e53a5e47 --- /dev/null +++ b/video-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/video-app/src/main/res/mipmap-hdpi/ic_launcher.webp b/video-app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000..c209e78ecd Binary files /dev/null and b/video-app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/video-app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/video-app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000..b2dfe3d1ba Binary files /dev/null and b/video-app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/video-app/src/main/res/mipmap-mdpi/ic_launcher.webp b/video-app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000..4f0f1d64e5 Binary files /dev/null and b/video-app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/video-app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/video-app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000000..62b611da08 Binary files /dev/null and b/video-app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/video-app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/video-app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000..948a3070fe Binary files /dev/null and b/video-app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/video-app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/video-app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000..1b9a6956b3 Binary files /dev/null and b/video-app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/video-app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/video-app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000000..28d4b77f9f Binary files /dev/null and b/video-app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/video-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/video-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000..9287f50836 Binary files /dev/null and b/video-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/video-app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/video-app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000000..aa7d6427e6 Binary files /dev/null and b/video-app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/video-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/video-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000..9126ae37cb Binary files /dev/null and b/video-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/video-app/src/main/res/values/colors.xml b/video-app/src/main/res/values/colors.xml new file mode 100644 index 0000000000..38279fc979 --- /dev/null +++ b/video-app/src/main/res/values/colors.xml @@ -0,0 +1,15 @@ + + + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/video-app/src/main/res/values/strings.xml b/video-app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..06af607989 --- /dev/null +++ b/video-app/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + + + Video Player + \ No newline at end of file diff --git a/video-app/src/main/res/values/themes.xml b/video-app/src/main/res/values/themes.xml new file mode 100644 index 0000000000..35ed1d0bff --- /dev/null +++ b/video-app/src/main/res/values/themes.xml @@ -0,0 +1,10 @@ + + + + + +