diff --git a/app/src/main/java/cash/z/ecc/android/ui/home/OgHomeFragment.kt b/app/src/main/java/cash/z/ecc/android/ui/home/OgHomeFragment.kt new file mode 100644 index 0000000..2ee4aba --- /dev/null +++ b/app/src/main/java/cash/z/ecc/android/ui/home/OgHomeFragment.kt @@ -0,0 +1,439 @@ +package cash.z.ecc.android.ui.home + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import androidx.lifecycle.lifecycleScope +import cash.z.ecc.android.R +import cash.z.ecc.android.databinding.FragmentHomeBinding +import cash.z.ecc.android.di.viewmodel.activityViewModel +import cash.z.ecc.android.di.viewmodel.viewModel +import cash.z.ecc.android.ext.* +import cash.z.ecc.android.feedback.Report +import cash.z.ecc.android.feedback.Report.Tap.* +import cash.z.ecc.android.ui.base.BaseFragment +import cash.z.ecc.android.ui.home.OgHomeFragment.BannerAction.* +import cash.z.ecc.android.ui.send.SendViewModel +import cash.z.ecc.android.ui.setup.WalletSetupViewModel +import cash.z.ecc.android.ui.setup.WalletSetupViewModel.WalletSetupState.NO_SEED +import cash.z.ecc.android.sdk.Synchronizer +import cash.z.ecc.android.sdk.Synchronizer.Status.* +import cash.z.ecc.android.sdk.block.CompactBlockProcessor +import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString +import cash.z.ecc.android.sdk.ext.convertZecToZatoshi +import cash.z.ecc.android.sdk.ext.safelyConvertToBigDecimal +import cash.z.ecc.android.sdk.ext.twig +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch + +class OgHomeFragment : BaseFragment() { + override val screen = Report.Screen.HOME + + private lateinit var numberPad: List + private lateinit var uiModel: HomeViewModel.UiModel + + private val walletSetup: WalletSetupViewModel by activityViewModel(false) + private val sendViewModel: SendViewModel by activityViewModel() + private val viewModel: HomeViewModel by viewModel() + + lateinit var snake: MagicSnakeLoader + + override fun inflate(inflater: LayoutInflater): FragmentHomeBinding = + FragmentHomeBinding.inflate(inflater) + + + // + // LifeCycle + // + + override fun onAttach(context: Context) { + twig("HomeFragment.onAttach") + twig("ZZZ") + twig("ZZZ") + twig("ZZZ") + twig("ZZZ ===================== HOME FRAGMENT CREATED ==================================") + super.onAttach(context) + + // this will call startSync either now or later (after initializing with newly created seed) + walletSetup.checkSeed().onEach { + twig("Checking seed") + if (it == NO_SEED) { + // interact with user to create, backup and verify seed + // leads to a call to startSync(), later (after accounts are created from seed) + twig("Seed not found, therefore, launching seed creation flow") + mainActivity?.safeNavigate(R.id.action_nav_home_to_nav_landing) + } else { + twig("Found seed. Re-opening existing wallet") + mainActivity?.startSync(walletSetup.openWallet()) + } + }.launchIn(lifecycleScope) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + twig("HomeFragment.onViewCreated uiModel: ${::uiModel.isInitialized} saved: ${savedInstanceState != null}") + with(binding) { + numberPad = arrayListOf( + buttonNumberPad0.asKey(), + buttonNumberPad1.asKey(), + buttonNumberPad2.asKey(), + buttonNumberPad3.asKey(), + buttonNumberPad4.asKey(), + buttonNumberPad5.asKey(), + buttonNumberPad6.asKey(), + buttonNumberPad7.asKey(), + buttonNumberPad8.asKey(), + buttonNumberPad9.asKey(), + buttonNumberPadDecimal.asKey(), + buttonNumberPadBack.asKey() + ) + hitAreaReceive.onClickNavTo(R.id.action_nav_home_to_nav_profile) { tapped(HOME_PROFILE) } + textDetail.onClickNavTo(R.id.action_nav_home_to_nav_detail) { tapped(HOME_DETAIL) } + hitAreaScan.setOnClickListener { + mainActivity?.maybeOpenScan().also { tapped(HOME_SCAN) } + } + + textBannerAction.setOnClickListener { + onBannerAction(BannerAction.from((it as? TextView)?.text?.toString())) + } + buttonSendAmount.setOnClickListener { + onSend().also { tapped(HOME_SEND) } + } + setSendAmount("0", false) + + snake = MagicSnakeLoader(binding.lottieButtonLoading) + } + + binding.buttonNumberPadBack.setOnLongClickListener { + onClearAmount().also { tapped(HOME_CLEAR_AMOUNT) } + true + } + + if (::uiModel.isInitialized) { + twig("uiModel exists!") + onModelUpdated(null, uiModel) + } + } + + private fun onClearAmount() { + if (::uiModel.isInitialized) { + resumedScope.launch { + binding.textSendAmount.text.apply { + while (uiModel.pendingSend != "0") { + viewModel.onChar('<') + delay(5) + } + } + } + } + } + + override fun onResume() { + super.onResume() +// twig("HomeFragment.onResume resumeScope.isActive: ${resumedScope.isActive} $resumedScope") +// viewModel.initializeMaybe() +// onClearAmount() +// viewModel.uiModels.scanReduce { old, new -> +// onModelUpdated(old, new) +// new +// }.onCompletion { +// twig("uiModel.scanReduce completed.") +// }.catch { e -> +// twig("exception while processing uiModels $e") +// throw e +// }.launchIn(resumedScope) +// +// // TODO: see if there is a better way to trigger a refresh of the uiModel on resume +// // the latest one should just be in the viewmodel and we should just "resubscribe" +// // but for some reason, this doesn't always happen, which kind of defeats the purpose +// // of having a cold stream in the view model +// resumedScope.launch { +// viewModel.refreshBalance() +// } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + twig("HomeFragment.onSaveInstanceState") + if (::uiModel.isInitialized) { +// outState.putParcelable("uiModel", uiModel) + } + } + + override fun onViewStateRestored(savedInstanceState: Bundle?) { + super.onViewStateRestored(savedInstanceState) + savedInstanceState?.let { inState -> + twig("HomeFragment.onViewStateRestored") +// onModelUpdated(HomeViewModel.UiModel(), inState.getParcelable("uiModel")!!) + } + } + + + // + // Public UI API + // + + var isSendEnabled = false + fun setSendEnabled(enabled: Boolean, isSynced: Boolean) { + isSendEnabled = enabled + binding.buttonSendAmount.apply { + if (enabled || !isSynced) { + isEnabled = true + isClickable = isSynced + binding.lottieButtonLoading.alpha = 1.0f + } else { + isEnabled = false + isClickable = false + binding.lottieButtonLoading.alpha = 0.32f + } + } + } + + fun setProgress(uiModel: HomeViewModel.UiModel) { + if (!uiModel.processorInfo.hasData && !uiModel.isDisconnected) { + twig("Warning: ignoring progress update because the processor is still starting.") + return + } + + snake.isSynced = uiModel.isSynced + if (!uiModel.isSynced) { + snake.downloadProgress = uiModel.downloadProgress + snake.scanProgress = uiModel.scanProgress + } + + val sendText = when { + uiModel.status == DISCONNECTED -> "Reconnecting . . ." + uiModel.isSynced -> if (uiModel.hasFunds) "SEND AMOUNT" else "NO FUNDS AVAILABLE" + uiModel.status == STOPPED -> "IDLE" + uiModel.isDownloading -> "Downloading . . . ${snake.downloadProgress}%" + uiModel.isValidating -> "Validating . . ." + uiModel.isScanning -> "Scanning . . . ${snake.scanProgress}%" + else -> "Updating" + } + + binding.buttonSendAmount.text = sendText + twig("Send button set to: $sendText") + + val resId = if (uiModel.isSynced) R.color.selector_button_text_dark else R.color.selector_button_text_light + binding.buttonSendAmount.setTextColor(resources.getColorStateList(resId)) + binding.lottieButtonLoading.invisibleIf(uiModel.isDisconnected) + } + + /** + * @param amount the amount to send represented as ZEC, without the dollar sign. + */ + fun setSendAmount(amount: String, updateModel: Boolean = true) { + binding.textSendAmount.text = "\$$amount".toColoredSpan(R.color.text_light_dimmed, "$") + if (updateModel) { + sendViewModel.zatoshiAmount = amount.safelyConvertToBigDecimal().convertZecToZatoshi() + } + binding.buttonSendAmount.disabledIf(amount == "0") + } + + fun setAvailable(availableBalance: Long = -1L, totalBalance: Long = -1L) { + val missingBalance = availableBalance < 0 + val availableString = if (missingBalance) "Updating" else availableBalance.convertZatoshiToZecString() + binding.textBalanceAvailable.text = availableString + binding.textBalanceAvailable.transparentIf(missingBalance) + binding.labelBalance.transparentIf(missingBalance) + binding.textBalanceDescription.apply { + goneIf(missingBalance) + text = if (availableBalance != -1L && (availableBalance < totalBalance)) { + val change = (totalBalance - availableBalance).convertZatoshiToZecString() + "(expecting +$change ZEC)".toColoredSpan(R.color.text_light, "+$change") + } else { + "(enter an amount to send)" + } + } + } + + fun setBanner(message: String = "", action: BannerAction = CLEAR) { + with(binding) { + val hasMessage = !message.isEmpty() || action != CLEAR + groupBalance.goneIf(hasMessage) + groupBanner.goneIf(!hasMessage) + layerLock.goneIf(!hasMessage) + + textBannerMessage.text = message + textBannerAction.text = action.action + } + } + + + // + // Private UI Events + // + + private fun onModelUpdated(old: HomeViewModel.UiModel?, new: HomeViewModel.UiModel) { + logUpdate(old, new) + uiModel = new + if (old?.pendingSend != new.pendingSend) { + setSendAmount(new.pendingSend) + } + setProgress(uiModel) // TODO: we may not need to separate anymore +// if (new.status = SYNCING) onSyncing(new) else onSynced(new) + if (new.status == SYNCED) onSynced(new) else onSyncing(new) + setSendEnabled(new.isSendEnabled, new.status == SYNCED) + } + + private fun logUpdate(old: HomeViewModel.UiModel?, new: HomeViewModel.UiModel) { + var message = "" + fun maybeComma() = if (message.length > "UiModel(".length) ", " else "" + message = when { + old == null -> "$new" + new == null -> "null" + else -> { + buildString { + append("UiModel(") + if (old.status != new.status) append ("status=${new.status}") + if (old.processorInfo != new.processorInfo) { + append ("${maybeComma()}processorInfo=ProcessorInfo(") + val startLength = length + fun innerComma() = if (length > startLength) ", " else "" + if (old.processorInfo.networkBlockHeight != new.processorInfo.networkBlockHeight) append("networkBlockHeight=${new.processorInfo.networkBlockHeight}") + if (old.processorInfo.lastScannedHeight != new.processorInfo.lastScannedHeight) append("${innerComma()}lastScannedHeight=${new.processorInfo.lastScannedHeight}") + if (old.processorInfo.lastDownloadedHeight != new.processorInfo.lastDownloadedHeight) append("${innerComma()}lastDownloadedHeight=${new.processorInfo.lastDownloadedHeight}") + if (old.processorInfo.lastDownloadRange != new.processorInfo.lastDownloadRange) append("${innerComma()}lastDownloadRange=${new.processorInfo.lastDownloadRange}") + if (old.processorInfo.lastScanRange != new.processorInfo.lastScanRange) append("${innerComma()}lastScanRange=${new.processorInfo.lastScanRange}") + append(")") + } + if (old.availableBalance != new.availableBalance) append ("${maybeComma()}availableBalance=${new.availableBalance}") + if (old.totalBalance != new.totalBalance) append ("${maybeComma()}totalBalance=${new.totalBalance}") + if (old.pendingSend != new.pendingSend) append ("${maybeComma()}pendingSend=${new.pendingSend}") + append(")") + } + } + } + twig("onModelUpdated: $message") + } + + private fun onSyncing(uiModel: HomeViewModel.UiModel) { + setAvailable() + } + + private fun onSynced(uiModel: HomeViewModel.UiModel) { + snake.isSynced = true + if (!uiModel.hasBalance) { + onNoFunds() + } else { + setBanner("") + setAvailable(uiModel.availableBalance, uiModel.totalBalance) + } + } + + private fun onSend() { + if (isSendEnabled) mainActivity?.safeNavigate(R.id.action_nav_home_to_send) + } + + private fun onBannerAction(action: BannerAction) { + when (action) { + FUND_NOW -> { + MaterialAlertDialogBuilder(requireActivity()) + .setMessage("To make full use of this wallet, deposit funds to your address.") + .setTitle("No Balance") + .setCancelable(true) + .setPositiveButton("View Address") { dialog, _ -> + tapped(HOME_FUND_NOW) + dialog.dismiss() + mainActivity?.safeNavigate(R.id.action_nav_home_to_nav_receive) + } + .show() +// MaterialAlertDialogBuilder(activity) +// .setMessage("To make full use of this wallet, deposit funds to your address or tap the faucet to trigger a tiny automatic deposit.\n\nFaucet funds are made available for the community by the community for testing. So please be kind enough to return what you borrow!") +// .setTitle("No Balance") +// .setCancelable(true) +// .setPositiveButton("Tap Faucet") { dialog, _ -> +// dialog.dismiss() +// setBanner("Tapping faucet...", CANCEL) +// } +// .setNegativeButton("View Address") { dialog, _ -> +// dialog.dismiss() +// mainActivity?.safeNavigate(R.id.action_nav_home_to_nav_receive) +// } +// .show() + } + CANCEL -> { + // TODO: trigger banner / balance update + onNoFunds() + } + } + } + + private fun onNoFunds() { + setBanner("No Balance", FUND_NOW) + } + + + // + // Inner classes and extensions + // + + enum class BannerAction(val action: String) { + FUND_NOW(""), + CANCEL("Cancel"), + NONE(""), + CLEAR("clear"); + + companion object { + fun from(action: String?): BannerAction { + values().forEach { + if (it.action == action) return it + } + throw IllegalArgumentException("Invalid BannerAction: $action") + } + } + } + + private fun TextView.asKey(): TextView { + val c = text[0] + setOnClickListener { + lifecycleScope.launch { + twig("CHAR TYPED: $c") + viewModel.onChar(c) + } + } + return this + } + + + + + // TODO: remove these troubleshooting logs + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + twig("HomeFragment.onCreate") + } + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + twig("HomeFragment.onActivityCreated") + } + override fun onStart() { + super.onStart() + twig("HomeFragment.onStart") + } + override fun onPause() { + super.onPause() + twig("HomeFragment.onPause resumeScope.isActive: ${resumedScope.isActive}") + } + override fun onStop() { + super.onStop() + twig("HomeFragment.onStop") + } + override fun onDestroyView() { + super.onDestroyView() + twig("HomeFragment.onDestroyView") + } + override fun onDestroy() { + super.onDestroy() + twig("HomeFragment.onDestroy") + } + override fun onDetach() { + super.onDetach() + twig("HomeFragment.onDetach") + } +} \ No newline at end of file diff --git a/app/src/main/java/cash/z/ecc/android/ui/zircle/InviteFragment.kt b/app/src/main/java/cash/z/ecc/android/ui/zircle/InviteFragment.kt new file mode 100644 index 0000000..1f04955 --- /dev/null +++ b/app/src/main/java/cash/z/ecc/android/ui/zircle/InviteFragment.kt @@ -0,0 +1,20 @@ +package cash.z.ecc.android.ui.zircle + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import cash.z.ecc.android.databinding.FragmentZircleInviteBinding +import cash.z.ecc.android.ext.onClickNavBack +import cash.z.ecc.android.ui.base.BaseFragment + + +class InviteFragment : BaseFragment() { + + override fun inflate(inflater: LayoutInflater): FragmentZircleInviteBinding = + FragmentZircleInviteBinding.inflate(inflater) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.backButtonHitArea.onClickNavBack() + } +} \ No newline at end of file diff --git a/app/src/main/java/cash/z/ecc/android/ui/zircle/NewZircleFragment.kt b/app/src/main/java/cash/z/ecc/android/ui/zircle/NewZircleFragment.kt new file mode 100644 index 0000000..072f174 --- /dev/null +++ b/app/src/main/java/cash/z/ecc/android/ui/zircle/NewZircleFragment.kt @@ -0,0 +1,26 @@ +package cash.z.ecc.android.ui.zircle + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import cash.z.ecc.android.R +import cash.z.ecc.android.databinding.FragmentZircleNewBinding +import cash.z.ecc.android.ext.onClickNavBack +import cash.z.ecc.android.ui.base.BaseFragment + + +class NewZircleFragment : BaseFragment() { + + override fun inflate(inflater: LayoutInflater): FragmentZircleNewBinding = + FragmentZircleNewBinding.inflate(inflater) + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.hitAreaScan.onClickNavBack() + binding.buttonBottom.setOnClickListener { + mainActivity?.safeNavigate(R.id.action_nav_new_to_nav_zircle) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/cash/z/ecc/android/ui/zircle/ZircleAdapter.kt b/app/src/main/java/cash/z/ecc/android/ui/zircle/ZircleAdapter.kt new file mode 100644 index 0000000..e67d470 --- /dev/null +++ b/app/src/main/java/cash/z/ecc/android/ui/zircle/ZircleAdapter.kt @@ -0,0 +1,45 @@ +package cash.z.ecc.android.ui.zircle + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.paging.PagedListAdapter +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import cash.z.ecc.android.R +import cash.z.ecc.android.sdk.db.entity.ConfirmedTransaction + +class ZircleAdapter : + ListAdapter( + object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: T, + newItem: T + ) = oldItem.minedHeight == newItem.minedHeight && oldItem.noteId == newItem.noteId + // bugfix: distinguish between self-transactions so they don't overwrite each other in the UI // TODO confirm that this is working, as intended + && ((oldItem.raw == null && newItem.raw == null) || (oldItem.raw != null && newItem.raw != null && oldItem.raw!!.contentEquals(newItem.raw!!))) + + override fun areContentsTheSame( + oldItem: T, + newItem: T + ) = oldItem == newItem + } + ) { + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ) = ZirclerViewHolder( + LayoutInflater.from(parent.context).inflate(R.layout.item_transaction, parent, false) + ) + + override fun onBindViewHolder( + holder: ZirclerViewHolder, + position: Int + ) = holder.bindTo(getItem(position)) +} + +class ZirclerViewHolder { + +} + +data class Zircler(val nickname: String) diff --git a/app/src/main/java/cash/z/ecc/android/ui/zircle/ZircleDetailFragment.kt b/app/src/main/java/cash/z/ecc/android/ui/zircle/ZircleDetailFragment.kt new file mode 100644 index 0000000..1409a5e --- /dev/null +++ b/app/src/main/java/cash/z/ecc/android/ui/zircle/ZircleDetailFragment.kt @@ -0,0 +1,36 @@ +package cash.z.ecc.android.ui.zircle + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import cash.z.ecc.android.databinding.FragmentZircleDetailBinding +import cash.z.ecc.android.ext.onClickNavBack +import cash.z.ecc.android.ui.base.BaseFragment +import cash.z.ecc.android.ui.detail.TransactionAdapter +import cash.z.ecc.android.ui.detail.TransactionsFooter + + +class ZircleDetailFragment : BaseFragment() { + + override fun inflate(inflater: LayoutInflater): FragmentZircleDetailBinding = + FragmentZircleDetailBinding.inflate(inflater) + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.backButtonHitArea.onClickNavBack() + + initRecycler() + } + + private fun initRecycler() { + binding.recyclerZirclers.apply { + layoutManager = + LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false) + adapter = TransactionAdapter() + smoothScrollToPosition(0) + } + } + +} \ No newline at end of file diff --git a/app/src/main/res/color/selector_debugger.xml b/app/src/main/res/color/selector_debugger.xml new file mode 100644 index 0000000..0cdc1c8 --- /dev/null +++ b/app/src/main/res/color/selector_debugger.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/selector_input_background.xml b/app/src/main/res/color/selector_input_background.xml new file mode 100644 index 0000000..45ce237 --- /dev/null +++ b/app/src/main/res/color/selector_input_background.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/selector_radio.xml b/app/src/main/res/color/selector_radio.xml new file mode 100644 index 0000000..6c82529 --- /dev/null +++ b/app/src/main/res/color/selector_radio.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/bg_invite.png b/app/src/main/res/drawable-hdpi/bg_invite.png new file mode 100644 index 0000000..e8ff034 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/bg_invite.png differ diff --git a/app/src/main/res/drawable-mdpi/bg_invite.png b/app/src/main/res/drawable-mdpi/bg_invite.png new file mode 100644 index 0000000..f3dd7d8 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/bg_invite.png differ diff --git a/app/src/main/res/drawable-xhdpi/bg_invite.png b/app/src/main/res/drawable-xhdpi/bg_invite.png new file mode 100644 index 0000000..d75e050 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/bg_invite.png differ diff --git a/app/src/main/res/drawable-xxhdpi/bg_invite.png b/app/src/main/res/drawable-xxhdpi/bg_invite.png new file mode 100644 index 0000000..2beddae Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bg_invite.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_invite.png b/app/src/main/res/drawable-xxxhdpi/bg_invite.png new file mode 100644 index 0000000..fa0cd05 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_invite.png differ diff --git a/app/src/main/res/drawable/background_bottom_sheet.xml b/app/src/main/res/drawable/background_bottom_sheet.xml new file mode 100644 index 0000000..50c6527 --- /dev/null +++ b/app/src/main/res/drawable/background_bottom_sheet.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_zircle_gradient_pressed.xml b/app/src/main/res/drawable/background_zircle_gradient_pressed.xml new file mode 100644 index 0000000..a1cad13 --- /dev/null +++ b/app/src/main/res/drawable/background_zircle_gradient_pressed.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/background_zircle_gradient_unpressed.xml b/app/src/main/res/drawable/background_zircle_gradient_unpressed.xml new file mode 100644 index 0000000..dd47a9f --- /dev/null +++ b/app/src/main/res/drawable/background_zircle_gradient_unpressed.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/app/src/main/res/drawable/background_zircle_solid_button.xml b/app/src/main/res/drawable/background_zircle_solid_button.xml new file mode 100644 index 0000000..30007d9 --- /dev/null +++ b/app/src/main/res/drawable/background_zircle_solid_button.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/gradient_progress.xml b/app/src/main/res/drawable/gradient_progress.xml new file mode 100644 index 0000000..40170fd --- /dev/null +++ b/app/src/main/res/drawable/gradient_progress.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_menu.xml b/app/src/main/res/drawable/ic_menu.xml new file mode 100644 index 0000000..be111fc --- /dev/null +++ b/app/src/main/res/drawable/ic_menu.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_piggy.xml b/app/src/main/res/drawable/ic_piggy.xml new file mode 100644 index 0000000..e1eba5a --- /dev/null +++ b/app/src/main/res/drawable/ic_piggy.xml @@ -0,0 +1,38 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_zircle_logo.xml b/app/src/main/res/drawable/ic_zircle_logo.xml new file mode 100644 index 0000000..0324829 --- /dev/null +++ b/app/src/main/res/drawable/ic_zircle_logo.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/selector_rounded_gradient_ripple.xml b/app/src/main/res/drawable/selector_rounded_gradient_ripple.xml new file mode 100644 index 0000000..0a1e5c6 --- /dev/null +++ b/app/src/main/res/drawable/selector_rounded_gradient_ripple.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/layout/fragment_zircle_detail.xml b/app/src/main/res/layout/fragment_zircle_detail.xml new file mode 100644 index 0000000..eac2c7a --- /dev/null +++ b/app/src/main/res/layout/fragment_zircle_detail.xml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_zircle_home.xml b/app/src/main/res/layout/fragment_zircle_home.xml new file mode 100644 index 0000000..df61fe3 --- /dev/null +++ b/app/src/main/res/layout/fragment_zircle_home.xml @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_zircle_invite.xml b/app/src/main/res/layout/fragment_zircle_invite.xml new file mode 100644 index 0000000..d3317e2 --- /dev/null +++ b/app/src/main/res/layout/fragment_zircle_invite.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_zircle_new.xml b/app/src/main/res/layout/fragment_zircle_new.xml new file mode 100644 index 0000000..6c227fe --- /dev/null +++ b/app/src/main/res/layout/fragment_zircle_new.xml @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_zircler.xml b/app/src/main/res/layout/item_zircler.xml new file mode 100644 index 0000000..66ecfaf --- /dev/null +++ b/app/src/main/res/layout/item_zircler.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + \ No newline at end of file