Skip to content

Commit

Permalink
Add dual UI system
Browse files Browse the repository at this point in the history
* Duplicate MainActivity, settings fragment and realted files.
* Add a setting to toggle between UIs and an activity launcher that
  launches the corresponding activity.
* Resolve DI and ToxService's notification intent
* Change the new theme to be based on material design.
  • Loading branch information
roihershberg committed Aug 5, 2022
1 parent f57f24b commit c214d3e
Show file tree
Hide file tree
Showing 18 changed files with 1,585 additions and 3 deletions.
7 changes: 6 additions & 1 deletion atox/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
android:resource="@xml/file_paths" />
</provider>

<activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTask">
<activity
android:name=".ActivityLauncher"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
Expand All @@ -57,5 +59,8 @@
<data android:mimeType="text/plain" />
</intent-filter>
</activity>

<activity android:name=".MainActivity" android:launchMode="singleTask" />
<activity android:name=".NewMainActivity" android:launchMode="singleTask" />
</application>
</manifest>
32 changes: 32 additions & 0 deletions atox/src/main/kotlin/ActivityLauncher.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ltd.evilcorp.atox

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import javax.inject.Inject
import ltd.evilcorp.atox.settings.Settings

class ActivityLauncher : Activity() {

@Inject
lateinit var settings: Settings

override fun onCreate(savedInstanceState: Bundle?) {
(application as App).component.inject(this)
super.onCreate(savedInstanceState)

val newIntent = intent.clone() as Intent

if (settings.wipUI) {
newIntent.setClass(applicationContext, NewMainActivity::class.java)
} else {
newIntent.setClass(applicationContext, MainActivity::class.java)
}
startActivity(newIntent)
}

override fun onStop() {
super.onStop()
finish()
}
}
2 changes: 1 addition & 1 deletion atox/src/main/kotlin/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fun Context.hasPermission(permission: String) =
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED

val Fragment.vmFactory: ViewModelFactory
get() = (requireActivity() as MainActivity).vmFactory
get() = (requireActivity() as? MainActivity)?.vmFactory ?: (requireActivity() as NewMainActivity).vmFactory

class NoSuchArgumentException(arg: String) : Exception("No such argument: $arg")

Expand Down
131 changes: 131 additions & 0 deletions atox/src/main/kotlin/NewMainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// SPDX-FileCopyrightText: 2019-2021 aTox contributors
//
// SPDX-License-Identifier: GPL-3.0-only

package ltd.evilcorp.atox

import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.bundleOf
import androidx.core.view.WindowCompat
import androidx.navigation.fragment.findNavController
import coil.Coil
import coil.ImageLoader
import coil.decode.GifDecoder
import coil.decode.ImageDecoderDecoder
import javax.inject.Inject
import ltd.evilcorp.atox.di.ViewModelFactory
import ltd.evilcorp.atox.settings.Settings
import ltd.evilcorp.atox.ui.contactlist.ARG_SHARE

private const val TAG = "OldMainActivity"
private const val SCHEME = "tox:"
private const val TOX_ID_LENGTH = 76

class NewMainActivity : AppCompatActivity() {
@Inject
lateinit var vmFactory: ViewModelFactory

@Inject
lateinit var autoAway: AutoAway

@Inject
lateinit var settings: Settings

override fun onCreate(savedInstanceState: Bundle?) {
(application as App).component.inject(this)

super.onCreate(savedInstanceState)

if (settings.disableScreenshots) {
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
}

AppCompatDelegate.setDefaultNightMode(settings.theme)

// The view inset/padding adjustments only run for Lollipop and newer.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
WindowCompat.setDecorFitsSystemWindows(window, false)
}

Coil.setImageLoader {
ImageLoader.Builder(this)
.componentRegistry {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
add(ImageDecoderDecoder(this@NewMainActivity))
} else {
add(GifDecoder())
}
}
.build()
}

setTheme(R.style.Theme_aTox_DayNight)
setContentView(R.layout.new_activity_main)

// Only handle intent the first time it triggers the app.
if (savedInstanceState != null) return
handleIntent(intent)
}

override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
handleIntent(intent)
}

override fun onPause() {
super.onPause()
autoAway.onBackground()
}

override fun onResume() {
super.onResume()
autoAway.onForeground()
}

private fun handleIntent(intent: Intent?) {
when (intent?.action) {
Intent.ACTION_VIEW -> handleToxLinkIntent(intent)
Intent.ACTION_SEND -> handleShareIntent(intent)
}
}

private fun handleToxLinkIntent(intent: Intent) {
val data = intent.dataString ?: ""
Log.i(TAG, "Got uri with data: $data")
if (!data.startsWith(SCHEME) || data.length != SCHEME.length + TOX_ID_LENGTH) {
Log.e(TAG, "Got malformed uri: $data")
return
}

supportFragmentManager.findFragmentById(R.id.nav_host_fragment)?.findNavController()?.navigate(
R.id.addContactFragment,
bundleOf("toxId" to data.drop(SCHEME.length)),
)
}

private fun handleShareIntent(intent: Intent) {
if (intent.type != "text/plain") {
Log.e(TAG, "Got unsupported share type ${intent.type}")
return
}

val data = intent.getStringExtra(Intent.EXTRA_TEXT)
if (data.isNullOrEmpty()) {
Log.e(TAG, "Got share intent with no data")
return
}

Log.i(TAG, "Got text share: $data")
val navController =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment)?.findNavController() ?: return
navController.navigate(R.id.contactListFragment, bundleOf(ARG_SHARE to data))
}
}
9 changes: 8 additions & 1 deletion atox/src/main/kotlin/ToxService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
import ltd.evilcorp.atox.settings.Settings
import ltd.evilcorp.atox.tox.ToxStarter
import ltd.evilcorp.core.repository.UserRepository
import ltd.evilcorp.core.vo.ConnectionStatus
Expand Down Expand Up @@ -58,6 +59,9 @@ class ToxService : LifecycleService() {
@Inject
lateinit var proximityScreenOff: ProximityScreenOff

@Inject
lateinit var settings: Settings

private fun createNotificationChannel() {
val channel = NotificationChannelCompat.Builder(channelId, NotificationManagerCompat.IMPORTANCE_LOW)
.setName("Tox Service")
Expand All @@ -74,7 +78,10 @@ class ToxService : LifecycleService() {

private fun notificationFor(status: ConnectionStatus?): Notification {
val pendingIntent: PendingIntent =
Intent(this, MainActivity::class.java).let { notificationIntent ->
Intent(
this,
if (settings.wipUI) NewMainActivity::class.java else MainActivity::class.java,
).let { notificationIntent ->
PendingIntentCompat.getActivity(this, 0, notificationIntent, 0)
}

Expand Down
4 changes: 4 additions & 0 deletions atox/src/main/kotlin/di/AppComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import android.content.Context
import dagger.BindsInstance
import dagger.Component
import ltd.evilcorp.atox.ActionReceiver
import ltd.evilcorp.atox.ActivityLauncher
import ltd.evilcorp.atox.BootReceiver
import ltd.evilcorp.atox.MainActivity
import ltd.evilcorp.atox.NewMainActivity
import ltd.evilcorp.atox.ToxService
import javax.inject.Singleton

Expand All @@ -29,7 +31,9 @@ interface AppComponent {
fun create(@BindsInstance appContext: Context): AppComponent
}

fun inject(activity: ActivityLauncher)
fun inject(activity: MainActivity)
fun inject(activity: NewMainActivity)
fun inject(service: ToxService)
fun inject(receiver: BootReceiver)
fun inject(receiver: ActionReceiver)
Expand Down
6 changes: 6 additions & 0 deletions atox/src/main/kotlin/di/ViewModelModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ltd.evilcorp.atox.ui.friendrequest.FriendRequestViewModel
import ltd.evilcorp.atox.ui.settings.SettingsViewModel
import ltd.evilcorp.atox.ui.userprofile.UserProfileViewModel
import kotlin.reflect.KClass
import ltd.evilcorp.atox.newui.settings.SettingsViewModel as NewSettingsViewModel

@MustBeDocumented
@Target(
Expand Down Expand Up @@ -73,6 +74,11 @@ abstract class ViewModelModule {
@ViewModelKey(SettingsViewModel::class)
abstract fun bindSettingsViewModel(vm: SettingsViewModel): ViewModel

@Binds
@IntoMap
@ViewModelKey(NewSettingsViewModel::class)
abstract fun bindNewSettingsViewModel(vm: NewSettingsViewModel): ViewModel

@Binds
@IntoMap
@ViewModelKey(UserProfileViewModel::class)
Expand Down
Loading

0 comments on commit c214d3e

Please sign in to comment.