From 03b88c5b4b2ffda8df67327baac53ba034006b76 Mon Sep 17 00:00:00 2001 From: tom5079 <7948651+tom5079@users.noreply.github.com> Date: Thu, 11 Apr 2024 07:53:38 -0700 Subject: [PATCH] wip --- app/build.gradle | 2 + app/src/main/AndroidManifest.xml | 7 + .../pupil/adapters/TransferPeersAdapter.kt | 44 +++ .../receiver/WifiDirectBroadcastReceiver.kt | 59 ++++ .../xyz/quaver/pupil/ui/TransferActivity.kt | 294 ++++++++++++++++++ .../pupil/ui/fragment/SettingsFragment.kt | 9 + .../ui/fragment/TransferDirectionFragment.kt | 44 +++ .../ui/fragment/TransferPermissionFragment.kt | 38 +++ .../ui/fragment/TransferTargetFragment.kt | 69 ++++ .../TransferWaitForConnectionFragment.kt | 32 ++ app/src/main/res/drawable/arrow.xml | 13 + app/src/main/res/drawable/link.xml | 13 + app/src/main/res/drawable/round_button.xml | 13 + app/src/main/res/drawable/transfer_device.xml | 12 + app/src/main/res/drawable/transfer_ripple.xml | 9 + app/src/main/res/drawable/warning.xml | 13 + app/src/main/res/layout/transfer_activity.xml | 7 + .../layout/transfer_direction_fragment.xml | 89 ++++++ .../res/layout/transfer_peer_list_item.xml | 32 ++ .../layout/transfer_permission_fragment.xml | 38 +++ .../res/layout/transfer_target_fragment.xml | 58 ++++ .../transfer_wait_for_connection_fragment.xml | 54 ++++ app/src/main/res/values-ja/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/root_preferences.xml | 4 + 26 files changed, 956 insertions(+) create mode 100644 app/src/main/java/xyz/quaver/pupil/adapters/TransferPeersAdapter.kt create mode 100644 app/src/main/java/xyz/quaver/pupil/receiver/WifiDirectBroadcastReceiver.kt create mode 100644 app/src/main/java/xyz/quaver/pupil/ui/TransferActivity.kt create mode 100644 app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferDirectionFragment.kt create mode 100644 app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferPermissionFragment.kt create mode 100644 app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferTargetFragment.kt create mode 100644 app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferWaitForConnectionFragment.kt create mode 100644 app/src/main/res/drawable/arrow.xml create mode 100644 app/src/main/res/drawable/link.xml create mode 100644 app/src/main/res/drawable/round_button.xml create mode 100644 app/src/main/res/drawable/transfer_device.xml create mode 100644 app/src/main/res/drawable/transfer_ripple.xml create mode 100644 app/src/main/res/drawable/warning.xml create mode 100644 app/src/main/res/layout/transfer_activity.xml create mode 100644 app/src/main/res/layout/transfer_direction_fragment.xml create mode 100644 app/src/main/res/layout/transfer_peer_list_item.xml create mode 100644 app/src/main/res/layout/transfer_permission_fragment.xml create mode 100644 app/src/main/res/layout/transfer_target_fragment.xml create mode 100644 app/src/main/res/layout/transfer_wait_for_connection_fragment.xml diff --git a/app/build.gradle b/app/build.gradle index e87d894f..bceb8214 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -127,6 +127,8 @@ dependencies { implementation "ru.noties.markwon:core:3.1.0" + implementation "com.skyfishjy.ripplebackground:library:1.0.1" + implementation "org.jsoup:jsoup:1.14.3" implementation "xyz.quaver:documentfilex:0.7.2" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5b07b068..640c0d6d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,6 +13,12 @@ + + + + @@ -179,6 +185,7 @@ + \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/TransferPeersAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/TransferPeersAdapter.kt new file mode 100644 index 00000000..4b95d64a --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/adapters/TransferPeersAdapter.kt @@ -0,0 +1,44 @@ +package xyz.quaver.pupil.adapters + +import android.net.wifi.p2p.WifiP2pDevice +import android.net.wifi.p2p.WifiP2pDeviceList +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import androidx.recyclerview.widget.RecyclerView +import xyz.quaver.pupil.R +import xyz.quaver.pupil.databinding.TransferPeerListItemBinding + +class TransferPeersAdapter( + private val devices: Collection, + private val onDeviceSelected: (WifiP2pDevice) -> Unit +): RecyclerView.Adapter() { + + class ViewHolder(val binding: TransferPeerListItemBinding): RecyclerView.ViewHolder(binding.root) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val binding = TransferPeerListItemBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + return ViewHolder(binding) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val device = devices.elementAt(position) + + holder.binding.deviceName.text = device.deviceName + holder.binding.deviceAddress.text = device.deviceAddress + + holder.binding.root.setOnClickListener { + onDeviceSelected(device) + } + } + + override fun getItemCount(): Int { + return devices.size + } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/receiver/WifiDirectBroadcastReceiver.kt b/app/src/main/java/xyz/quaver/pupil/receiver/WifiDirectBroadcastReceiver.kt new file mode 100644 index 00000000..55eebdd3 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/receiver/WifiDirectBroadcastReceiver.kt @@ -0,0 +1,59 @@ +package xyz.quaver.pupil.receiver + +import android.Manifest +import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.wifi.p2p.WifiP2pManager +import android.os.Build +import android.os.Parcelable +import android.util.Log +import androidx.core.app.ActivityCompat +import xyz.quaver.pupil.ui.ErrorType +import xyz.quaver.pupil.ui.TransferStep +import xyz.quaver.pupil.ui.TransferViewModel + +private inline fun Intent.getParcelableExtraCompat(key: String): T? = when { + Build.VERSION.SDK_INT >= 33 -> getParcelableExtra(key, T::class.java) + else -> @Suppress("DEPRECATION") getParcelableExtra(key) as? T +} + +class WifiDirectBroadcastReceiver( + private val manager: WifiP2pManager, + private val channel: WifiP2pManager.Channel, + private val viewModel: TransferViewModel +): BroadcastReceiver() { + @SuppressLint("MissingPermission") + override fun onReceive(context: Context?, intent: Intent?) { + context!! + when (intent?.action) { + WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> { + val state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1) + viewModel.setWifiP2pEnabled(state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) + } + WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> { + manager.requestPeers(channel) { peers -> + viewModel.setPeers(peers) + } + } + WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> { + // Respond to new connection or disconnections + val networkInfo = intent.getParcelableExtraCompat(WifiP2pManager.EXTRA_NETWORK_INFO) + + if (networkInfo?.isConnected == true) { + manager.requestConnectionInfo(channel) { info -> + viewModel.setConnectionInfo(info) + } + } else { + viewModel.setConnectionInfo(null) + } + } + WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> { + // Respond to this device's wifi state changing + viewModel.setThisDevice(intent.getParcelableExtraCompat(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/TransferActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/TransferActivity.kt new file mode 100644 index 00000000..50d61087 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/TransferActivity.kt @@ -0,0 +1,294 @@ +package xyz.quaver.pupil.ui + +import android.Manifest +import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.IntentFilter +import android.content.pm.ActivityInfo +import android.content.pm.PackageManager +import android.net.wifi.WpsInfo +import android.net.wifi.p2p.WifiP2pConfig +import android.net.wifi.p2p.WifiP2pDevice +import android.net.wifi.p2p.WifiP2pDeviceList +import android.net.wifi.p2p.WifiP2pInfo +import android.net.wifi.p2p.WifiP2pManager +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES +import android.os.Bundle +import android.util.Log +import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.fragment.app.commit +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import xyz.quaver.pupil.R +import xyz.quaver.pupil.receiver.WifiDirectBroadcastReceiver +import xyz.quaver.pupil.ui.fragment.TransferDirectionFragment +import xyz.quaver.pupil.ui.fragment.TransferPermissionFragment +import xyz.quaver.pupil.ui.fragment.TransferTargetFragment +import xyz.quaver.pupil.ui.fragment.TransferWaitForConnectionFragment +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +class TransferActivity : AppCompatActivity(R.layout.transfer_activity) { + + private val viewModel: TransferViewModel by viewModels() + + private val intentFilter = IntentFilter().apply { + addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION) + addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION) + addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION) + addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION) + } + + private lateinit var manager: WifiP2pManager + private lateinit var channel: WifiP2pManager.Channel + + private var receiver: BroadcastReceiver? = null + + private val requestPermissionLauncher = registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { granted -> + if (granted) { + viewModel.setStep(TransferStep.TARGET) + } else { + viewModel.setStep(TransferStep.PERMISSION) + } + } + + private fun checkPermission(force: Boolean = false): Boolean { + val permissionRequired = if (VERSION.SDK_INT < VERSION_CODES.TIRAMISU) { + Manifest.permission.ACCESS_FINE_LOCATION + } else { + Manifest.permission.NEARBY_WIFI_DEVICES + } + + val permissionGranted = + ActivityCompat.checkSelfPermission(this, permissionRequired) == PackageManager.PERMISSION_GRANTED + + val shouldShowRationale = + ActivityCompat.shouldShowRequestPermissionRationale(this, permissionRequired) + + if (!permissionGranted) { + if (shouldShowRationale && force) { + viewModel.setStep(TransferStep.PERMISSION) + } else { + requestPermissionLauncher.launch(permissionRequired) + } + return false + } + + return true + } + + @SuppressLint("SourceLockedOrientationActivity") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + supportActionBar?.hide() + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + + manager = getSystemService(WIFI_P2P_SERVICE) as WifiP2pManager + channel = manager.initialize(this, mainLooper, null) + + viewModel.peerToConnect.observe(this) { peer -> + if (peer == null) { return@observe } + if (!checkPermission()) { return@observe } + + val config = WifiP2pConfig().apply { + deviceAddress = peer.deviceAddress + wps.setup = WpsInfo.PBC + } + + manager.connect(channel, config, object: WifiP2pManager.ActionListener { + override fun onSuccess() { + Log.d("PUPILD", "Connection successful") + } + + override fun onFailure(reason: Int) { + Log.d("PUPILD", "Connection failed: $reason") + viewModel.setPeers(null) + } + }) + } + + viewModel.connectionInfo.observe(this) { info -> + if (info == null) { return@observe } + + if (info.groupFormed && info.isGroupOwner) { + // Do something + Log.d("PUPILD", "Group formed and is group owner") + Log.d("PUPILD", "Group owner IP: ${info.groupOwnerAddress.hostAddress}") + } else if (info.groupFormed) { + // Do something + Log.d("PUPILD", "Group formed") + Log.d("PUPILD", "Group owner IP: ${info.groupOwnerAddress.hostAddress}") + Log.d("PUPILD", "Local IP: ${info.groupOwnerAddress.hostAddress}") + Log.d("PUPILD", "Is group owner: ${info.isGroupOwner}") + } + } + + lifecycleScope.launch { + viewModel.step.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED).collect { step -> + when (step) { + TransferStep.TARGET, + TransferStep.TARGET_FORCE -> { + if (!checkPermission(step == TransferStep.TARGET_FORCE)) { + return@collect + } + + manager.discoverPeers(channel, object: WifiP2pManager.ActionListener { + override fun onSuccess() { } + + override fun onFailure(reason: Int) { + } + }) + + supportFragmentManager.commit { + replace(R.id.fragment_container_view, TransferTargetFragment()) + } + } + TransferStep.DIRECTION -> { + supportFragmentManager.commit { + replace(R.id.fragment_container_view, TransferDirectionFragment()) + } + } + TransferStep.PERMISSION -> { + supportFragmentManager.commit { + replace(R.id.fragment_container_view, TransferPermissionFragment()) + } + } + TransferStep.WAIT_FOR_CONNECTION -> { + if (!checkPermission()) { return@collect } + + runCatching { + suspendCoroutine { continuation -> + manager.removeGroup(channel, object: WifiP2pManager.ActionListener { + override fun onSuccess() { + continuation.resume(Unit) + } + + override fun onFailure(reason: Int) { + continuation.resume(Unit) + } + }) + } + + suspendCoroutine { continuation -> + manager.cancelConnect(channel, object: WifiP2pManager.ActionListener { + override fun onSuccess() { + continuation.resume(Unit) + } + + override fun onFailure(reason: Int) { + continuation.resume(Unit) + } + }) + } + + suspendCoroutine { continuation -> + manager.createGroup(channel, object: WifiP2pManager.ActionListener { + override fun onSuccess() { + continuation.resume(Unit) + } + + override fun onFailure(reason: Int) { + continuation.resumeWithException(Exception("Failed to create group $reason")) + } + }) + } + }.onFailure { + Log.e("PUPILD", "Failed to create group", it) + } + + supportFragmentManager.commit { + replace(R.id.fragment_container_view, TransferWaitForConnectionFragment()) + } + } + } + } + } + + } + + override fun onResume() { + super.onResume() + WifiDirectBroadcastReceiver(manager, channel, viewModel).also { + receiver = it + registerReceiver(it, intentFilter) + } + } + + override fun onPause() { + super.onPause() + receiver?.let { unregisterReceiver(it) } + receiver = null + } +} + +enum class TransferStep { + TARGET, TARGET_FORCE, DIRECTION, PERMISSION, WAIT_FOR_CONNECTION +} + +enum class ErrorType { +} + +class TransferViewModel : ViewModel() { + private val _step: MutableStateFlow = MutableStateFlow(TransferStep.DIRECTION) + val step: StateFlow = _step + + private val _error = MutableLiveData(null) + val error: LiveData = _error + + private val _wifiP2pEnabled: MutableLiveData = MutableLiveData(false) + val wifiP2pEnabled: LiveData = _wifiP2pEnabled + + private val _thisDevice: MutableLiveData = MutableLiveData(null) + val thisDevice: LiveData = _thisDevice + + private val _peers: MutableLiveData = MutableLiveData(null) + val peers: LiveData = _peers + + private val _connectionInfo: MutableLiveData = MutableLiveData(null) + val connectionInfo: LiveData = _connectionInfo + + private val _peerToConnect: MutableLiveData = MutableLiveData(null) + val peerToConnect: LiveData = _peerToConnect + + fun setStep(step: TransferStep) { + _step.value = step + } + + fun setWifiP2pEnabled(enabled: Boolean) { + _wifiP2pEnabled.value = enabled + } + + fun setThisDevice(device: WifiP2pDevice?) { + _thisDevice.value = device + } + + fun setPeers(peers: WifiP2pDeviceList?) { + _peers.value = peers + } + + fun setConnectionInfo(info: WifiP2pInfo?) { + _connectionInfo.value = info + } + + fun setError(error: ErrorType?) { + _error.value = error + } + + fun connect(device: WifiP2pDevice) { + _peerToConnect.value = device + } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt index 97706f8b..e1cd9a30 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt @@ -21,12 +21,14 @@ package xyz.quaver.pupil.ui.fragment import android.app.Activity import android.content.* import android.os.Bundle +import android.util.Log import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatDelegate import androidx.preference.* import com.google.android.gms.oss.licenses.OssLicensesMenuActivity import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.google.firebase.crashlytics.internal.model.CrashlyticsReport import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -40,6 +42,7 @@ import xyz.quaver.pupil.clientHolder import xyz.quaver.pupil.types.SendLogException import xyz.quaver.pupil.ui.LockActivity import xyz.quaver.pupil.ui.SettingsActivity +import xyz.quaver.pupil.ui.TransferActivity import xyz.quaver.pupil.ui.dialog.* import xyz.quaver.pupil.util.* import xyz.quaver.pupil.util.downloader.DownloadManager @@ -113,6 +116,9 @@ class SettingsFragment : ) Toast.makeText(context, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() } + "transfer_data" -> { + activity?.startActivity(Intent(activity, TransferActivity::class.java)) + } else -> return false } } @@ -300,6 +306,9 @@ class SettingsFragment : true } } + "transfer_data" -> { + onPreferenceClickListener = this@SettingsFragment + } } } diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferDirectionFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferDirectionFragment.kt new file mode 100644 index 00000000..27584eda --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferDirectionFragment.kt @@ -0,0 +1,44 @@ +package xyz.quaver.pupil.ui.fragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import xyz.quaver.pupil.R +import xyz.quaver.pupil.databinding.TransferDirectionFragmentBinding +import xyz.quaver.pupil.ui.TransferStep +import xyz.quaver.pupil.ui.TransferViewModel + +class TransferDirectionFragment : Fragment(R.layout.transfer_direction_fragment) { + + private var _binding: TransferDirectionFragmentBinding? = null + private val binding get() = _binding!! + + private val viewModel: TransferViewModel by activityViewModels() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = TransferDirectionFragmentBinding.inflate(inflater, container, false) + + binding.inButton.setOnClickListener { + viewModel.setStep(TransferStep.TARGET) + } + + binding.outButton.setOnClickListener { + viewModel.setStep(TransferStep.WAIT_FOR_CONNECTION) + } + + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferPermissionFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferPermissionFragment.kt new file mode 100644 index 00000000..e6155814 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferPermissionFragment.kt @@ -0,0 +1,38 @@ +package xyz.quaver.pupil.ui.fragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import xyz.quaver.pupil.databinding.TransferPermissionFragmentBinding +import xyz.quaver.pupil.ui.TransferStep +import xyz.quaver.pupil.ui.TransferViewModel + +class TransferPermissionFragment: Fragment() { + + private var _binding: TransferPermissionFragmentBinding? = null + private val binding get() = _binding!! + + private val viewModel: TransferViewModel by activityViewModels() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = TransferPermissionFragmentBinding.inflate(inflater, container, false) + + binding.permissionsButton.setOnClickListener { + viewModel.setStep(TransferStep.TARGET_FORCE) + } + + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferTargetFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferTargetFragment.kt new file mode 100644 index 00000000..70584dba --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferTargetFragment.kt @@ -0,0 +1,69 @@ +package xyz.quaver.pupil.ui.fragment + +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import xyz.quaver.pupil.R +import xyz.quaver.pupil.adapters.TransferPeersAdapter +import xyz.quaver.pupil.databinding.TransferTargetFragmentBinding +import xyz.quaver.pupil.ui.TransferStep +import xyz.quaver.pupil.ui.TransferViewModel + +class TransferTargetFragment : Fragment() { + + private var _binding: TransferTargetFragmentBinding? = null + private val binding get() = _binding!! + + private val viewModel: TransferViewModel by activityViewModels() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = TransferTargetFragmentBinding.inflate(inflater, container, false) + + viewModel.thisDevice.observe(viewLifecycleOwner) { device -> + if (device == null) { + return@observe + } + + if (device.status == 3) { + binding.ripple.startRippleAnimation() + binding.retryButton.visibility = View.INVISIBLE + } else { + binding.ripple.stopRippleAnimation() + binding.retryButton.visibility = View.VISIBLE + } + } + + viewModel.peers.observe(viewLifecycleOwner) { peers -> + if (peers == null) { + return@observe + } + + binding.deviceList.adapter = TransferPeersAdapter(peers.deviceList) { + viewModel.connect(it) + } + } + + binding.ripple.startRippleAnimation() + + binding.retryButton.setOnClickListener { + viewModel.setStep(TransferStep.TARGET) + } + + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferWaitForConnectionFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferWaitForConnectionFragment.kt new file mode 100644 index 00000000..41f81cc4 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/fragment/TransferWaitForConnectionFragment.kt @@ -0,0 +1,32 @@ +package xyz.quaver.pupil.ui.fragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import xyz.quaver.pupil.databinding.TransferWaitForConnectionFragmentBinding + +class TransferWaitForConnectionFragment : Fragment() { + + private var _binding: TransferWaitForConnectionFragmentBinding? = null + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + _binding = TransferWaitForConnectionFragmentBinding.inflate(layoutInflater) + + binding.ripple.startRippleAnimation() + + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/arrow.xml b/app/src/main/res/drawable/arrow.xml new file mode 100644 index 00000000..f4eda35b --- /dev/null +++ b/app/src/main/res/drawable/arrow.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/link.xml b/app/src/main/res/drawable/link.xml new file mode 100644 index 00000000..8e1158ad --- /dev/null +++ b/app/src/main/res/drawable/link.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/round_button.xml b/app/src/main/res/drawable/round_button.xml new file mode 100644 index 00000000..b167415e --- /dev/null +++ b/app/src/main/res/drawable/round_button.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/transfer_device.xml b/app/src/main/res/drawable/transfer_device.xml new file mode 100644 index 00000000..f202c9ab --- /dev/null +++ b/app/src/main/res/drawable/transfer_device.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/transfer_ripple.xml b/app/src/main/res/drawable/transfer_ripple.xml new file mode 100644 index 00000000..db299b54 --- /dev/null +++ b/app/src/main/res/drawable/transfer_ripple.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/warning.xml b/app/src/main/res/drawable/warning.xml new file mode 100644 index 00000000..c5cbb208 --- /dev/null +++ b/app/src/main/res/drawable/warning.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/layout/transfer_activity.xml b/app/src/main/res/layout/transfer_activity.xml new file mode 100644 index 00000000..9ddc1a47 --- /dev/null +++ b/app/src/main/res/layout/transfer_activity.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/transfer_direction_fragment.xml b/app/src/main/res/layout/transfer_direction_fragment.xml new file mode 100644 index 00000000..3089f3fc --- /dev/null +++ b/app/src/main/res/layout/transfer_direction_fragment.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/transfer_peer_list_item.xml b/app/src/main/res/layout/transfer_peer_list_item.xml new file mode 100644 index 00000000..6f576f5c --- /dev/null +++ b/app/src/main/res/layout/transfer_peer_list_item.xml @@ -0,0 +1,32 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/transfer_permission_fragment.xml b/app/src/main/res/layout/transfer_permission_fragment.xml new file mode 100644 index 00000000..7f6ceb72 --- /dev/null +++ b/app/src/main/res/layout/transfer_permission_fragment.xml @@ -0,0 +1,38 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/transfer_target_fragment.xml b/app/src/main/res/layout/transfer_target_fragment.xml new file mode 100644 index 00000000..25a94a12 --- /dev/null +++ b/app/src/main/res/layout/transfer_target_fragment.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/transfer_wait_for_connection_fragment.xml b/app/src/main/res/layout/transfer_wait_for_connection_fragment.xml new file mode 100644 index 00000000..8fdd34e2 --- /dev/null +++ b/app/src/main/res/layout/transfer_wait_for_connection_fragment.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index ea44ad2f..1b320e63 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -159,4 +159,5 @@ アンドロイド11以上では外部からのアプリ内部空間接近が不可能です。ダウンロードフォルダを変更しますか? ネットワーク ダウンロードデータベースを再構築 + 他の機器にデータを転送 \ No newline at end of file diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 8554abb8..583f3f15 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -159,4 +159,5 @@ 안드로이드 11 이상에서는 외부에서 현재 다운로드 폴더에 접근할 수 없습니다. 변경하시겠습니까? 네트워크 다운로드 데이터베이스 복구 + 다른 기기에 데이터 전송 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c2cc2bbf..3a60e8c4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -174,6 +174,7 @@ Hide image from gallery Low quality images Load low quality images to improve load speed and data usage + Transfer data to another device diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index de11f055..9446e355 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -52,6 +52,10 @@ app:defaultValue="8" app:useSimpleSummaryProvider="true"/> + +