From 49a65451a1cc42b2e07e6febe88148a9225264c7 Mon Sep 17 00:00:00 2001 From: P1V4HH2CFG Date: Wed, 2 Nov 2022 12:20:57 +0530 Subject: [PATCH 01/28] imported requestly-android-okhttp in requestly-android --- requestly-android/build.gradle | 1 + sample/build.gradle | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/requestly-android/build.gradle b/requestly-android/build.gradle index eb4d686..5bd5896 100644 --- a/requestly-android/build.gradle +++ b/requestly-android/build.gradle @@ -15,6 +15,7 @@ android { dependencies { api project(':requestly-android-core') api project(':requestly-android-event') + api project(':requestly-android-okhttp') } apply from: rootProject.file('gradle/gradle-mvn-push.gradle') diff --git a/sample/build.gradle b/sample/build.gradle index a36647e..e994e06 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -63,9 +63,6 @@ android { } dependencies { - debugImplementation project(':requestly-android-okhttp') - releaseImplementation project(':requestly-android-okhttp-noop') - debugImplementation project(':requestly-android') releaseImplementation project(':requestly-android-noop') From d2c2a158a4fbd027b1d3324722675a73c4d978f9 Mon Sep 17 00:00:00 2001 From: P1V4HH2CFG Date: Fri, 4 Nov 2022 14:02:50 +0530 Subject: [PATCH 02/28] created fragment, adaptor and ui --- .../android/core/KeyValueStorageManager.kt | 22 ++++ .../SharedPrefLineItemAdaptor.kt | 60 ++++++++++ .../SharedPrefViewerFragment.kt | 38 +++++++ .../SharedPrefViewerViewModel.kt | 7 ++ .../layout/fragment_shared_pref_viewer.xml | 17 +++ .../main/res/layout/host_switcher_item.xml | 1 + .../main/res/layout/shared_pref_line_item.xml | 103 ++++++++++++++++++ .../src/main/res/values/strings.xml | 2 + 8 files changed, 250 insertions(+) create mode 100644 requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefLineItemAdaptor.kt create mode 100644 requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt create mode 100644 requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerViewModel.kt create mode 100644 requestly-android-core/src/main/res/layout/fragment_shared_pref_viewer.xml create mode 100644 requestly-android-core/src/main/res/layout/shared_pref_line_item.xml diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/KeyValueStorageManager.kt b/requestly-android-core/src/main/java/io/requestly/android/core/KeyValueStorageManager.kt index 354029b..8c2be16 100644 --- a/requestly-android-core/src/main/java/io/requestly/android/core/KeyValueStorageManager.kt +++ b/requestly-android-core/src/main/java/io/requestly/android/core/KeyValueStorageManager.kt @@ -4,16 +4,24 @@ import android.content.Context import android.content.SharedPreferences import com.google.gson.Gson import com.google.gson.reflect.TypeToken +import java.io.File +import java.lang.ref.WeakReference object KeyValueStorageManager { private const val FILE_NAME = "io.requestly.requestly_pref_file" + /** + * Full path to the default directory assigned to the package for its + * persistent data. + */ + private lateinit var mContext: WeakReference private lateinit var mSharedPref: SharedPreferences private lateinit var gson: Gson private var changeListeners: MutableList = mutableListOf() fun initialize(context: Context) { + mContext = WeakReference(context) mSharedPref = context.getSharedPreferences(FILE_NAME, 0) gson = Gson() } @@ -59,4 +67,18 @@ object KeyValueStorageManager { changeListeners.add(listener) mSharedPref.registerOnSharedPreferenceChangeListener(listener) } + + fun fetchDataFromAllSharedPrefFiles() { + val context = mContext.get() ?: return + + val prefsDir = File(context.applicationInfo.dataDir, "shared_prefs") + if(prefsDir.exists() && prefsDir.isDirectory) { + prefsDir.list()?.forEach { + val sharedPreferences = context.getSharedPreferences(it.removeSuffix(".xml"), 0) + sharedPreferences.all + } + } + } + } + diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefLineItemAdaptor.kt b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefLineItemAdaptor.kt new file mode 100644 index 0000000..d98406d --- /dev/null +++ b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefLineItemAdaptor.kt @@ -0,0 +1,60 @@ +package io.requestly.android.core.modules.sharedPrefViewer + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.requestly.android.core.databinding.SharedPrefLineItemBinding + +data class SharedPrefLineItemModel( + val dataTypeText: String, + val fileName: String, + val prefKeyText: String, + val prefValueText: String, + val onEditClickListener: (() -> Unit)?, + val onDeleteClickListener: (() -> Unit)? +) + +class SharedPrefLineItemAdaptor(var items: List) : + RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { + val viewBinding = SharedPrefLineItemBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + return ItemViewHolder(viewBinding) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.bindTo(items[position]) + } + + override fun getItemCount(): Int { + return items.size + } + + class ItemViewHolder(itemView: SharedPrefLineItemBinding) : + RecyclerView.ViewHolder(itemView.root) { + + private val dataTypeTextView = itemView.dataTypeTextView + private val fileNameTextView = itemView.fileNameTextView + private val prefKeyTextView = itemView.prefKeyTextView + private val prefValueTextView = itemView.prefValueTextView + private val editPrefButton = itemView.editPrefButton + private val deletePrefButton = itemView.deletePrefButton + + fun bindTo(model: SharedPrefLineItemModel) { + dataTypeTextView.text = model.dataTypeText + fileNameTextView.text = model.prefKeyText + prefKeyTextView.text = model.prefKeyText + prefValueTextView.text = model.prefValueText + editPrefButton.setOnClickListener { + model.onEditClickListener?.let { it1 -> it1() } + } + deletePrefButton.setOnClickListener { + model.onDeleteClickListener?.let { it1 -> it1() } + } + } + } +} diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt new file mode 100644 index 0000000..91dbf49 --- /dev/null +++ b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt @@ -0,0 +1,38 @@ +package io.requestly.android.core.modules.sharedPrefViewer + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.LinearLayoutManager +import io.requestly.android.core.databinding.FragmentSharedPrefViewerBinding + +class SharedPrefViewerFragment : Fragment() { + + private lateinit var mainBinding: FragmentSharedPrefViewerBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + mainBinding = FragmentSharedPrefViewerBinding.inflate(layoutInflater) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + mainBinding = FragmentSharedPrefViewerBinding.inflate(layoutInflater) + + // Inflate the layout for this fragment + initRecyclerView() + + return mainBinding.root + } + + private fun initRecyclerView() { + mainBinding.sharedPrefsEntryRecyclerView.layoutManager = LinearLayoutManager(requireContext()) + val adaptor = SharedPrefLineItemAdaptor(emptyList()) + mainBinding.sharedPrefsEntryRecyclerView.adapter = adaptor + } +} diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerViewModel.kt b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerViewModel.kt new file mode 100644 index 0000000..286410d --- /dev/null +++ b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerViewModel.kt @@ -0,0 +1,7 @@ +package io.requestly.android.core.modules.sharedPrefViewer + +import androidx.lifecycle.ViewModel + +class SharedPrefViewerViewModel: ViewModel() { + +} diff --git a/requestly-android-core/src/main/res/layout/fragment_shared_pref_viewer.xml b/requestly-android-core/src/main/res/layout/fragment_shared_pref_viewer.xml new file mode 100644 index 0000000..6700414 --- /dev/null +++ b/requestly-android-core/src/main/res/layout/fragment_shared_pref_viewer.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/requestly-android-core/src/main/res/layout/host_switcher_item.xml b/requestly-android-core/src/main/res/layout/host_switcher_item.xml index 20b455b..8a1ea73 100644 --- a/requestly-android-core/src/main/res/layout/host_switcher_item.xml +++ b/requestly-android-core/src/main/res/layout/host_switcher_item.xml @@ -81,6 +81,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="16dp" + android:layout_marginEnd="16dp" android:singleLine="false" android:text="TextView" android:textSize="18sp" diff --git a/requestly-android-core/src/main/res/layout/shared_pref_line_item.xml b/requestly-android-core/src/main/res/layout/shared_pref_line_item.xml new file mode 100644 index 0000000..73fb704 --- /dev/null +++ b/requestly-android-core/src/main/res/layout/shared_pref_line_item.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/requestly-android-core/src/main/res/values/strings.xml b/requestly-android-core/src/main/res/values/strings.xml index 1680082..b1879b8 100644 --- a/requestly-android-core/src/main/res/values/strings.xml +++ b/requestly-android-core/src/main/res/values/strings.xml @@ -9,4 +9,6 @@ Clear Add New Logcat filter + + Hello blank fragment From 9feb922d93cd03803c612b5043ac35c50f9ff0cc Mon Sep 17 00:00:00 2001 From: P1V4HH2CFG Date: Wed, 9 Nov 2022 08:05:20 +0530 Subject: [PATCH 03/28] shared pref logic completed --- .../android/core/KeyValueStorageManager.kt | 127 ++++++++++++++- .../SharedPrefLineItemAdaptor.kt | 5 +- .../SharedPrefViewerFragment.kt | 148 +++++++++++++++++- .../SharedPrefViewerViewModel.kt | 111 ++++++++++++- .../src/main/res/drawable/edit_text_box.xml | 15 ++ .../main/res/drawable/edit_text_box_alert.xml | 15 ++ .../main/res/drawable/ic_baseline_add_24.xml | 5 + .../res/drawable/ic_baseline_storage_24.xml | 5 + .../layout/fragment_shared_pref_viewer.xml | 18 ++- .../layout/shared_pref_edit_value_view.xml | 104 ++++++++++++ .../main/res/layout/shared_pref_line_item.xml | 52 +++++- .../src/main/res/menu/bottom_nav_menu.xml | 5 + .../main/res/navigation/main_nav_graph.xml | 1 + .../navigation/shared_pref_viewer_graph.xml | 15 ++ .../src/main/res/values/nav_ids.xml | 2 + .../requestly/android/sample/MainActivity.kt | 18 ++- 16 files changed, 625 insertions(+), 21 deletions(-) create mode 100644 requestly-android-core/src/main/res/drawable/edit_text_box.xml create mode 100644 requestly-android-core/src/main/res/drawable/edit_text_box_alert.xml create mode 100644 requestly-android-core/src/main/res/drawable/ic_baseline_add_24.xml create mode 100644 requestly-android-core/src/main/res/drawable/ic_baseline_storage_24.xml create mode 100644 requestly-android-core/src/main/res/layout/shared_pref_edit_value_view.xml create mode 100644 requestly-android-core/src/main/res/navigation/shared_pref_viewer_graph.xml diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/KeyValueStorageManager.kt b/requestly-android-core/src/main/java/io/requestly/android/core/KeyValueStorageManager.kt index 8c2be16..0b1cd51 100644 --- a/requestly-android-core/src/main/java/io/requestly/android/core/KeyValueStorageManager.kt +++ b/requestly-android-core/src/main/java/io/requestly/android/core/KeyValueStorageManager.kt @@ -7,6 +7,36 @@ import com.google.gson.reflect.TypeToken import java.io.File import java.lang.ref.WeakReference +data class SharedPrefFileData( + val key: String, + val value: Any?, + val dataType: SharedPrefType?, + val fileName: String, +) + +enum class SharedPrefType { + STRING { + override fun toString() = "String" + }, + INTEGER { + override fun toString() = "Integer" + }, + DOUBLE { + override fun toString() = "Double" + }, + LONG { + override fun toString() = "Long" + }, + BOOLEAN { + override fun toString() = "Boolean" + }, + STRING_SET { + override fun toString() = "StringSet" + }; + + abstract override fun toString(): String +} + object KeyValueStorageManager { private const val FILE_NAME = "io.requestly.requestly_pref_file" @@ -18,7 +48,8 @@ object KeyValueStorageManager { private lateinit var mContext: WeakReference private lateinit var mSharedPref: SharedPreferences private lateinit var gson: Gson - private var changeListeners: MutableList = mutableListOf() + private var changeListeners: MutableList = + mutableListOf() fun initialize(context: Context) { mContext = WeakReference(context) @@ -58,6 +89,63 @@ object KeyValueStorageManager { return gson.fromJson(json, typeToken.type) } + fun deleteKeyFromFile(keyName: String, fileName: String) { + val pref = mContext.get()?.getSharedPreferences(fileName, 0) ?: return + + with(pref.edit()) { + this.remove(keyName) + this.commit() + } + } + + fun putStringInfile(value: String, keyName: String, fileName: String) { + val pref = mContext.get()?.getSharedPreferences(fileName, 0) ?: return + with(pref.edit()) { + this.putString(keyName, value) + this.commit() + } + } + + fun putStringSetInfile(value: Set, keyName: String, fileName: String) { + val pref = mContext.get()?.getSharedPreferences(fileName, 0) ?: return + with(pref.edit()) { + this.putStringSet(keyName, value) + this.commit() + } + } + + fun putIntegerInfile(value: Int, keyName: String, fileName: String) { + val pref = mContext.get()?.getSharedPreferences(fileName, 0) ?: return + with(pref.edit()) { + this.putInt(keyName, value) + this.commit() + } + } + + fun putDoubleInfile(value: Double, keyName: String, fileName: String) { + val pref = mContext.get()?.getSharedPreferences(fileName, 0) ?: return + with(pref.edit()) { + this.putFloat(keyName, value.toFloat()) + this.commit() + } + } + + fun putLongInfile(value: Long, keyName: String, fileName: String) { + val pref = mContext.get()?.getSharedPreferences(fileName, 0) ?: return + with(pref.edit()) { + this.putLong(keyName, value) + this.commit() + } + } + + fun putBooleanInfile(value: Boolean, keyName: String, fileName: String) { + val pref = mContext.get()?.getSharedPreferences(fileName, 0) ?: return + with(pref.edit()) { + this.putBoolean(keyName, value) + this.commit() + } + } + fun registerChangeListener(forKey: String, changeListener: () -> Unit) { val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key -> if (key === forKey) { @@ -68,17 +156,40 @@ object KeyValueStorageManager { mSharedPref.registerOnSharedPreferenceChangeListener(listener) } - fun fetchDataFromAllSharedPrefFiles() { - val context = mContext.get() ?: return + @Suppress("CANDIDATE_CHOSEN_USING_OVERLOAD_RESOLUTION_BY_LAMBDA_ANNOTATION") + fun fetchDataFromAllSharedPrefFiles(): List { + val context = mContext.get() ?: return emptyList() val prefsDir = File(context.applicationInfo.dataDir, "shared_prefs") - if(prefsDir.exists() && prefsDir.isDirectory) { - prefsDir.list()?.forEach { - val sharedPreferences = context.getSharedPreferences(it.removeSuffix(".xml"), 0) - sharedPreferences.all + + if (!prefsDir.exists() || !prefsDir.isDirectory) return emptyList() + + return prefsDir.list()?.flatMap { filename -> + val fileNameWithOutExtension = filename.removeSuffix(".xml") + val thisPref = context.getSharedPreferences(fileNameWithOutExtension, 0) + return@flatMap thisPref.all.map { entry -> + SharedPrefFileData( + key = entry.key, + value = entry.value, + dataType = detectType(entry.value), + fileName = fileNameWithOutExtension, + ) } - } + } ?: emptyList() } + private fun detectType(value: Any?): SharedPrefType? { + if (value == null) return null + + return when (value::class.simpleName) { + "Int" -> SharedPrefType.INTEGER + "Float" -> SharedPrefType.DOUBLE + "Long" -> SharedPrefType.LONG + "HashSet" -> SharedPrefType.STRING_SET + "Boolean" -> SharedPrefType.BOOLEAN + "String" -> SharedPrefType.STRING + else -> null + } + } } diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefLineItemAdaptor.kt b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefLineItemAdaptor.kt index d98406d..c06261c 100644 --- a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefLineItemAdaptor.kt +++ b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefLineItemAdaptor.kt @@ -1,6 +1,7 @@ package io.requestly.android.core.modules.sharedPrefViewer import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import io.requestly.android.core.databinding.SharedPrefLineItemBinding @@ -43,12 +44,14 @@ class SharedPrefLineItemAdaptor(var items: List) : private val prefValueTextView = itemView.prefValueTextView private val editPrefButton = itemView.editPrefButton private val deletePrefButton = itemView.deletePrefButton + private val prefValueExtraInfoTextView = itemView.prefValueExtraInfoTextView fun bindTo(model: SharedPrefLineItemModel) { dataTypeTextView.text = model.dataTypeText - fileNameTextView.text = model.prefKeyText + fileNameTextView.text = model.fileName prefKeyTextView.text = model.prefKeyText prefValueTextView.text = model.prefValueText + prefValueExtraInfoTextView.visibility = View.GONE editPrefButton.setOnClickListener { model.onEditClickListener?.let { it1 -> it1() } } diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt index 91dbf49..1c01127 100644 --- a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt +++ b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt @@ -1,16 +1,30 @@ package io.requestly.android.core.modules.sharedPrefViewer +import android.annotation.SuppressLint +import android.app.AlertDialog +import android.app.Dialog import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.WindowManager +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.EditText +import android.widget.TextView import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager +import io.requestly.android.core.R +import io.requestly.android.core.SharedPrefFileData import io.requestly.android.core.databinding.FragmentSharedPrefViewerBinding class SharedPrefViewerFragment : Fragment() { + private lateinit var mainBinding: FragmentSharedPrefViewerBinding + private lateinit var mRecyclerViewAdaptor: SharedPrefLineItemAdaptor + private val viewModel: SharedPrefViewerViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -26,13 +40,143 @@ class SharedPrefViewerFragment : Fragment() { // Inflate the layout for this fragment initRecyclerView() + initFileSelectorSpinner() return mainBinding.root } + private fun initFileSelectorSpinner() { + val files = viewModel.prefFilesNamesLive.value?.toMutableList() ?: emptyList() + val adaptor = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, files) + adaptor.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + + with(mainBinding.fileSelectSpinner) { + this.adapter = adaptor + this.setSelection(files.indexOf(viewModel.mSelectedFileName)) + this.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) { + + } + + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + viewModel.fileNameSelected(files[position]) + } + } + } + + viewModel.prefFilesNamesLive.observe(viewLifecycleOwner) { + adaptor.clear() + adaptor.addAll(it) + adaptor.notifyDataSetChanged() + mainBinding.fileSelectSpinner.setSelection(it.indexOf(viewModel.mSelectedFileName)) + } + } + + // Its fine to use notifyDataSetChanged here. + // Data size is small enough to not cause Frame skips or lags. + @SuppressLint("NotifyDataSetChanged") private fun initRecyclerView() { - mainBinding.sharedPrefsEntryRecyclerView.layoutManager = LinearLayoutManager(requireContext()) - val adaptor = SharedPrefLineItemAdaptor(emptyList()) + mainBinding.sharedPrefsEntryRecyclerView.layoutManager = + LinearLayoutManager(requireContext()) + + val mapper: (SharedPrefFileData) -> SharedPrefLineItemModel = { + SharedPrefLineItemModel( + dataTypeText = it.dataType.toString(), + fileName = it.fileName, + prefKeyText = it.key, + prefValueText = it.value.toString(), + onEditClickListener = { + showEditSharedPrefEntryDialog(it) { newValue -> + return@showEditSharedPrefEntryDialog viewModel.verifyAndSave( + newValue, + it + ) + } + }, + onDeleteClickListener = { + loadDeleteConfirmationDialog { + viewModel.deleteEntry(fileName = it.fileName, keyName = it.key) + } + } + ) + } + val adaptor = SharedPrefLineItemAdaptor(viewModel.prefEntriesLive.value?.map(mapper) ?: emptyList()) mainBinding.sharedPrefsEntryRecyclerView.adapter = adaptor + + viewModel.prefEntriesLive.observe(viewLifecycleOwner) { list -> + adaptor.items = list.map(mapper) + adaptor.notifyDataSetChanged() + } + } + + private fun showEditSharedPrefEntryDialog( + model: SharedPrefFileData, + onSaveClick: (String) -> Boolean + ) { + // Create Dialog & Set its params + val dialog = Dialog(requireContext()) + dialog.setContentView(R.layout.shared_pref_edit_value_view) + dialog.window?.setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + dialog.setCancelable(false) + + // Find all Views and configure them + val saveButton = dialog.findViewById(R.id.saveTextButton) + val cancelButton = dialog.findViewById(R.id.cancelTextButton) + val fileNameTextView = dialog.findViewById(R.id.fileNameTextView2) + val dataTypeTextView = dialog.findViewById(R.id.dataTypeTextView2) + val prefKeyTextView = dialog.findViewById(R.id.prefKeyTextView2) + val alertTextView = dialog.findViewById(R.id.alertTextView) + alertTextView.visibility = View.GONE + val prefValueEditTextBox = dialog.findViewById(R.id.prefValueEditTextBox) + + prefKeyTextView.text = model.key + fileNameTextView.text = model.fileName + dataTypeTextView.text = model.dataType.toString() + prefValueEditTextBox.setText(model.value.toString(), TextView.BufferType.EDITABLE) + prefValueEditTextBox.setSelection(prefValueEditTextBox.length()) + + prefValueEditTextBox.requestFocus() + saveButton.setOnClickListener { + val success = onSaveClick(prefValueEditTextBox.text.toString()) + if (success) { + prefValueEditTextBox.setBackgroundResource(R.drawable.edit_text_box) + alertTextView.visibility = View.GONE + dialog.hide() + } else { + prefValueEditTextBox.setBackgroundResource(R.drawable.edit_text_box_alert) + alertTextView.text = "Invalid Value for type ${model.dataType.toString()}" + alertTextView.visibility = View.VISIBLE + } + } + cancelButton.setOnClickListener { + dialog.hide() + } + + // Show Dialog + dialog.show() + } + + private fun loadDeleteConfirmationDialog(onPositiveButtonClick: () -> Unit) { + AlertDialog.Builder(requireActivity()) + .setCancelable(false) + .setTitle("Are you sure you want to delete this?") + .setPositiveButton("Yes") { dialog, _ -> + onPositiveButtonClick() + dialog.cancel() + } + .setNegativeButton("No") { dialog, _ -> + dialog.cancel() + } + .create() + .show() } } diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerViewModel.kt b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerViewModel.kt index 286410d..c23ed61 100644 --- a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerViewModel.kt +++ b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerViewModel.kt @@ -1,7 +1,116 @@ package io.requestly.android.core.modules.sharedPrefViewer +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import io.requestly.android.core.KeyValueStorageManager +import io.requestly.android.core.SharedPrefFileData +import io.requestly.android.core.SharedPrefType -class SharedPrefViewerViewModel: ViewModel() { +class SharedPrefViewerViewModel : ViewModel() { + private val CONST_ALL_FILES = "ALL" + private var _prefEntriesLive = MutableLiveData>() + val prefEntriesLive: LiveData> = _prefEntriesLive + + private var _prefFilesNamesLive = MutableLiveData>() + var prefFilesNamesLive: LiveData> = _prefFilesNamesLive + + var mSelectedFileName = CONST_ALL_FILES + private set + + init { + loadUIData() + } + + fun deleteEntry(fileName: String, keyName: String) { + KeyValueStorageManager.deleteKeyFromFile(keyName, fileName) + loadUIData() + } + + fun fileNameSelected(fileName: String) { + mSelectedFileName = fileName + loadUIData() + } + + private fun loadUIData() { + val entireData = KeyValueStorageManager.fetchDataFromAllSharedPrefFiles() + _prefEntriesLive.value = entireData.filter { + it.fileName == mSelectedFileName || (mSelectedFileName == CONST_ALL_FILES) + } + _prefFilesNamesLive.value = + (entireData.map { it.fileName }.distinct() + CONST_ALL_FILES).reversed() + } + + fun verifyAndSave(newValue: String, prefFileData: SharedPrefFileData): Boolean { + prefFileData.dataType ?: return false + var success = false + when (prefFileData.dataType) { + SharedPrefType.STRING -> { + KeyValueStorageManager.putStringInfile( + newValue, + prefFileData.key, + prefFileData.fileName + ) + success = true + } + SharedPrefType.INTEGER -> { + newValue.toIntOrNull()?.let { + KeyValueStorageManager.putIntegerInfile( + it, + prefFileData.key, + prefFileData.fileName + ) + success = true + } + } + SharedPrefType.DOUBLE -> { + newValue.toDoubleOrNull()?.let { + KeyValueStorageManager.putDoubleInfile( + it, + prefFileData.key, + prefFileData.fileName + ) + success = true + } + } + SharedPrefType.LONG -> { + newValue.toLongOrNull()?.let { + KeyValueStorageManager.putLongInfile( + it, + prefFileData.key, + prefFileData.fileName + ) + success = true + } + } + SharedPrefType.BOOLEAN -> { + newValue.toBooleanStrictOrNull()?.let { + KeyValueStorageManager.putBooleanInfile( + it, + prefFileData.key, + prefFileData.fileName + ) + success = true + } + } + SharedPrefType.STRING_SET -> { + if (newValue[0] == '[' && newValue[newValue.length - 1] == ']') { + val stringSet = newValue + .subSequence(1, newValue.length - 1) + .split(",") + .map { it.trim() } + .toSet() + KeyValueStorageManager.putStringSetInfile( + stringSet, + prefFileData.key, + prefFileData.fileName + ) + success = true + } + } + } + loadUIData() + return success + } } diff --git a/requestly-android-core/src/main/res/drawable/edit_text_box.xml b/requestly-android-core/src/main/res/drawable/edit_text_box.xml new file mode 100644 index 0000000..bd354d2 --- /dev/null +++ b/requestly-android-core/src/main/res/drawable/edit_text_box.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/requestly-android-core/src/main/res/drawable/edit_text_box_alert.xml b/requestly-android-core/src/main/res/drawable/edit_text_box_alert.xml new file mode 100644 index 0000000..c2d70de --- /dev/null +++ b/requestly-android-core/src/main/res/drawable/edit_text_box_alert.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/requestly-android-core/src/main/res/drawable/ic_baseline_add_24.xml b/requestly-android-core/src/main/res/drawable/ic_baseline_add_24.xml new file mode 100644 index 0000000..70046c4 --- /dev/null +++ b/requestly-android-core/src/main/res/drawable/ic_baseline_add_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/requestly-android-core/src/main/res/drawable/ic_baseline_storage_24.xml b/requestly-android-core/src/main/res/drawable/ic_baseline_storage_24.xml new file mode 100644 index 0000000..c8faeb2 --- /dev/null +++ b/requestly-android-core/src/main/res/drawable/ic_baseline_storage_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/requestly-android-core/src/main/res/layout/fragment_shared_pref_viewer.xml b/requestly-android-core/src/main/res/layout/fragment_shared_pref_viewer.xml index 6700414..98a8e74 100644 --- a/requestly-android-core/src/main/res/layout/fragment_shared_pref_viewer.xml +++ b/requestly-android-core/src/main/res/layout/fragment_shared_pref_viewer.xml @@ -4,6 +4,21 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + app:layout_constraintTop_toBottomOf="@id/divider" /> diff --git a/requestly-android-core/src/main/res/layout/shared_pref_edit_value_view.xml b/requestly-android-core/src/main/res/layout/shared_pref_edit_value_view.xml new file mode 100644 index 0000000..ed110a1 --- /dev/null +++ b/requestly-android-core/src/main/res/layout/shared_pref_edit_value_view.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + diff --git a/requestly-android-core/src/main/res/layout/shared_pref_line_item.xml b/requestly-android-core/src/main/res/layout/shared_pref_line_item.xml index 73fb704..2546673 100644 --- a/requestly-android-core/src/main/res/layout/shared_pref_line_item.xml +++ b/requestly-android-core/src/main/res/layout/shared_pref_line_item.xml @@ -3,17 +3,17 @@ 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:layout_height="wrap_content"> @@ -22,12 +22,12 @@ android:id="@+id/fileNameTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="8dp" + android:layout_marginTop="16dp" android:layout_marginEnd="8dp" android:background="#FAD4D4" android:padding="4dp" - android:text="TextView" - android:textColor="#FF5D5D" + android:text="FILENAME" + android:textColor="#B71C1C" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -37,6 +37,8 @@ android:layout_height="wrap_content" android:layout_marginStart="8dp" android:text="Key" + android:textAllCaps="true" + android:textColor="#73777B" android:textSize="14sp" app:layout_constraintBottom_toBottomOf="@+id/prefKeyTextView" app:layout_constraintStart_toStartOf="parent" @@ -48,6 +50,8 @@ android:layout_height="wrap_content" android:layout_marginStart="8dp" android:text="Value" + android:textAllCaps="true" + android:textColor="#73777B" android:textSize="14sp" app:layout_constraintBottom_toBottomOf="@+id/prefValueTextView" app:layout_constraintStart_toStartOf="parent" @@ -60,6 +64,8 @@ android:layout_marginStart="24dp" android:layout_marginTop="16dp" android:layout_marginEnd="8dp" + android:textColor="@color/black" + android:textStyle="italic" android:text="TextView" android:textSize="18sp" app:layout_constraintEnd_toEndOf="parent" @@ -72,12 +78,32 @@ android:layout_height="wrap_content" android:layout_marginTop="16dp" android:layout_marginEnd="8dp" + android:ellipsize="end" + android:maxLines="4" android:text="TextView" + android:textColor="@color/black" android:textSize="18sp" + android:textStyle="italic" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@+id/prefKeyTextView" app:layout_constraintTop_toBottomOf="@+id/prefKeyTextView" /> + + @@ -96,8 +126,18 @@ android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:text="Delete" + android:textAllCaps="true" + android:textColor="#0000EE" + android:textSize="16sp" + android:textStyle="bold" app:layout_constraintBottom_toBottomOf="@+id/editPrefButton" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/editPrefButton" /> + + diff --git a/requestly-android-core/src/main/res/menu/bottom_nav_menu.xml b/requestly-android-core/src/main/res/menu/bottom_nav_menu.xml index 03d63f5..ce49fb7 100644 --- a/requestly-android-core/src/main/res/menu/bottom_nav_menu.xml +++ b/requestly-android-core/src/main/res/menu/bottom_nav_menu.xml @@ -20,4 +20,9 @@ android:id="@id/logs_flow" android:icon="@drawable/ic_console_24" android:title="Logs" /> + + diff --git a/requestly-android-core/src/main/res/navigation/main_nav_graph.xml b/requestly-android-core/src/main/res/navigation/main_nav_graph.xml index 0f14cb7..e26d45a 100644 --- a/requestly-android-core/src/main/res/navigation/main_nav_graph.xml +++ b/requestly-android-core/src/main/res/navigation/main_nav_graph.xml @@ -8,6 +8,7 @@ + + + + + + + diff --git a/requestly-android-core/src/main/res/values/nav_ids.xml b/requestly-android-core/src/main/res/values/nav_ids.xml index bd6e4bd..c3e0691 100644 --- a/requestly-android-core/src/main/res/values/nav_ids.xml +++ b/requestly-android-core/src/main/res/values/nav_ids.xml @@ -4,9 +4,11 @@ + + diff --git a/sample/src/main/kotlin/io/requestly/android/sample/MainActivity.kt b/sample/src/main/kotlin/io/requestly/android/sample/MainActivity.kt index 7a7e5dc..e27cb6b 100644 --- a/sample/src/main/kotlin/io/requestly/android/sample/MainActivity.kt +++ b/sample/src/main/kotlin/io/requestly/android/sample/MainActivity.kt @@ -7,9 +7,9 @@ import android.text.method.LinkMovementMethod import android.view.View import androidx.appcompat.app.AppCompatActivity import io.requestly.android.event.api.RequestlyEvent +import io.requestly.android.okhttp.api.RQ import io.requestly.android.okhttp.api.RQ.getLaunchIntent import io.requestly.android.sample.databinding.ActivityMainSampleBinding -import io.requestly.android.okhttp.api.RQ private val interceptorTypeSelector = InterceptorTypeSelector() @@ -100,6 +100,22 @@ class MainActivity : AppCompatActivity() { .penaltyDeath() .build() ) + + val spFile1 = getSharedPreferences("com.sample.shared_pref_file_1", 0) + with(spFile1.edit()) { + this.putBoolean("bool_value", true) + this.putString("string_value", "Hello World") + this.putFloat("float_value", 3.14F) + this.putInt("integer_value", 100) + this.putLong("long_value", 1000000000000L) + this.commit() + } + + val spFile2 = getSharedPreferences("com.sample.shared_pref_file_2", 0) + with(spFile2.edit()) { + this.putStringSet("string_set_value", setOf("One", "Two", "Three", "Four")) + this.commit() + } } From 8f7ca581f1104a2710e7a016eebe9d330e23b45a Mon Sep 17 00:00:00 2001 From: P1V4HH2CFG Date: Wed, 9 Nov 2022 08:06:13 +0530 Subject: [PATCH 04/28] minor improvement in Host Switcher --- .../hostSwitcher/HostSwitcherFragment.kt | 2 + .../main/res/layout/host_switcher_item.xml | 45 ++++++++++++++----- .../res/menu/host_switcher_fragment_menu.xml | 2 +- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/modules/hostSwitcher/HostSwitcherFragment.kt b/requestly-android-core/src/main/java/io/requestly/android/core/modules/hostSwitcher/HostSwitcherFragment.kt index 4c7aa28..e94d063 100644 --- a/requestly-android-core/src/main/java/io/requestly/android/core/modules/hostSwitcher/HostSwitcherFragment.kt +++ b/requestly-android-core/src/main/java/io/requestly/android/core/modules/hostSwitcher/HostSwitcherFragment.kt @@ -87,6 +87,8 @@ class HostSwitcherFragment : Fragment() { val adaptor = HostSwitchItemAdaptor(items) viewModel.rulesListLive.observe(viewLifecycleOwner) { adaptor.items = it.map(mapper) + // Its fine to use notifyDataSetChanged here. + // Data size is small enough to not cause Frame skips or lags. adaptor.notifyDataSetChanged() } mainBinding.hostSwitcherRulesRecyclerView.adapter = adaptor diff --git a/requestly-android-core/src/main/res/layout/host_switcher_item.xml b/requestly-android-core/src/main/res/layout/host_switcher_item.xml index 8a1ea73..bb514ee 100644 --- a/requestly-android-core/src/main/res/layout/host_switcher_item.xml +++ b/requestly-android-core/src/main/res/layout/host_switcher_item.xml @@ -9,9 +9,11 @@ android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="16dp" + android:layout_marginStart="8dp" android:text="Replaces" - android:textSize="12sp" + android:textAllCaps="true" + android:textColor="#73777B" + android:textSize="14sp" app:layout_constraintBottom_toBottomOf="@+id/startingTextView" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/startingTextView" /> @@ -20,9 +22,12 @@ android:id="@+id/deleteButton" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginEnd="16dp" - android:textSize="16dp" + android:layout_marginEnd="8dp" android:text="Delete" + android:textAllCaps="true" + android:textColor="#0000EE" + android:textSize="16sp" + android:textStyle="bold" app:layout_constraintBottom_toBottomOf="@+id/editButton" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/editButton" /> @@ -32,8 +37,11 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="24dp" - android:textSize="16dp" android:text="Edit" + android:textAllCaps="true" + android:textColor="#0000EE" + android:textSize="16sp" + android:textStyle="bold" app:layout_constraintBottom_toBottomOf="@+id/activeSwitch" app:layout_constraintEnd_toStartOf="@+id/deleteButton" app:layout_constraintTop_toTopOf="@+id/activeSwitch" /> @@ -42,11 +50,14 @@ android:id="@+id/activeSwitch" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="16dp" + android:layout_marginStart="8dp" android:layout_marginTop="24dp" android:layout_marginBottom="16dp" android:minHeight="48dp" android:text="Active" + android:textAllCaps="true" + android:textColor="#73777B" + android:textStyle="bold" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/provisionalTextView" /> @@ -55,9 +66,11 @@ android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="16dp" + android:layout_marginStart="8dp" android:text="With" - android:textSize="12sp" + android:textAllCaps="true" + android:textColor="#73777B" + android:textSize="14sp" app:layout_constraintBottom_toBottomOf="@+id/provisionalTextView" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/provisionalTextView" /> @@ -68,10 +81,12 @@ android:layout_height="wrap_content" android:layout_marginStart="24dp" android:layout_marginTop="16dp" - android:layout_marginEnd="16dp" + android:layout_marginEnd="8dp" android:singleLine="false" android:text="TextView" + android:textColor="@color/black" android:textSize="18sp" + android:textStyle="italic" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/textView" app:layout_constraintTop_toTopOf="parent" /> @@ -81,12 +96,22 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="16dp" - android:layout_marginEnd="16dp" + android:layout_marginEnd="8dp" android:singleLine="false" android:text="TextView" + android:textColor="@color/black" android:textSize="18sp" + android:textStyle="italic" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="@+id/startingTextView" app:layout_constraintTop_toBottomOf="@+id/startingTextView" /> + + + + diff --git a/requestly-android-core/src/main/res/menu/host_switcher_fragment_menu.xml b/requestly-android-core/src/main/res/menu/host_switcher_fragment_menu.xml index dea07fb..1ffc95d 100644 --- a/requestly-android-core/src/main/res/menu/host_switcher_fragment_menu.xml +++ b/requestly-android-core/src/main/res/menu/host_switcher_fragment_menu.xml @@ -3,7 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android"> From 2ecec7a021648e410d630db44f8e8b57dc088047 Mon Sep 17 00:00:00 2001 From: P1V4HH2CFG Date: Wed, 9 Nov 2022 08:13:37 +0530 Subject: [PATCH 05/28] hide requestly shared pref files --- .../android/core/KeyValueStorageManager.kt | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/KeyValueStorageManager.kt b/requestly-android-core/src/main/java/io/requestly/android/core/KeyValueStorageManager.kt index 0b1cd51..d147a80 100644 --- a/requestly-android-core/src/main/java/io/requestly/android/core/KeyValueStorageManager.kt +++ b/requestly-android-core/src/main/java/io/requestly/android/core/KeyValueStorageManager.kt @@ -39,7 +39,8 @@ enum class SharedPrefType { object KeyValueStorageManager { - private const val FILE_NAME = "io.requestly.requestly_pref_file" + private const val REQUESTLY_SHARED_PREF_FILE_PREFIX = "io.requestly." + private const val FILE_NAME = "${REQUESTLY_SHARED_PREF_FILE_PREFIX}requestly_pref_file" /** * Full path to the default directory assigned to the package for its @@ -164,18 +165,21 @@ object KeyValueStorageManager { if (!prefsDir.exists() || !prefsDir.isDirectory) return emptyList() - return prefsDir.list()?.flatMap { filename -> - val fileNameWithOutExtension = filename.removeSuffix(".xml") - val thisPref = context.getSharedPreferences(fileNameWithOutExtension, 0) - return@flatMap thisPref.all.map { entry -> - SharedPrefFileData( - key = entry.key, - value = entry.value, - dataType = detectType(entry.value), - fileName = fileNameWithOutExtension, - ) - } - } ?: emptyList() + return prefsDir + .list() + ?.filter { !it.startsWith(REQUESTLY_SHARED_PREF_FILE_PREFIX) } + ?.flatMap { filename -> + val fileNameWithOutExtension = filename.removeSuffix(".xml") + val thisPref = context.getSharedPreferences(fileNameWithOutExtension, 0) + return@flatMap thisPref.all.map { entry -> + SharedPrefFileData( + key = entry.key, + value = entry.value, + dataType = detectType(entry.value), + fileName = fileNameWithOutExtension, + ) + } + } ?: emptyList() } private fun detectType(value: Any?): SharedPrefType? { From b9eaba3f14f9529f78bad40a1d8a73c773b643f4 Mon Sep 17 00:00:00 2001 From: P1V4HH2CFG Date: Wed, 9 Nov 2022 08:15:44 +0530 Subject: [PATCH 06/28] comments added --- .../core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt index 1c01127..5b2d7e2 100644 --- a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt +++ b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt @@ -46,6 +46,8 @@ class SharedPrefViewerFragment : Fragment() { } private fun initFileSelectorSpinner() { + // Keeping a copy of the list, because `adaptor.clear()` removes all entries from the + // live data also. Ideally this should be handled in viewModel itself. val files = viewModel.prefFilesNamesLive.value?.toMutableList() ?: emptyList() val adaptor = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, files) adaptor.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); From 0e3466c76c8d99eb95bbdeffe542c124f2a424fc Mon Sep 17 00:00:00 2001 From: P1V4HH2CFG Date: Wed, 9 Nov 2022 08:16:29 +0530 Subject: [PATCH 07/28] removed unused code --- .../core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt index 5b2d7e2..1dc0448 100644 --- a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt +++ b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt @@ -23,7 +23,6 @@ class SharedPrefViewerFragment : Fragment() { private lateinit var mainBinding: FragmentSharedPrefViewerBinding - private lateinit var mRecyclerViewAdaptor: SharedPrefLineItemAdaptor private val viewModel: SharedPrefViewerViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { From 9d42a5a4f16c3adf0ca5c8cd77bac3d13a7f6dd9 Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Fri, 4 Nov 2022 12:37:51 +0530 Subject: [PATCH 08/28] chore: bump version to 2.4.2 --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 954d323..b87d5d1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,9 +19,9 @@ org.gradle.parallel=true android.useAndroidX=true -VERSION_NAME=2.4.1 +VERSION_NAME=2.4.2 # 1*100*100 + 0*100 + 0 => 10000 -VERSION_CODE=20401 +VERSION_CODE=20402 GROUP=io.requestly POM_REPO_NAME=Requestly Android SDK From afb9b74bb9ceede7b813e90019265bfbb1740ae4 Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Sat, 5 Nov 2022 12:24:18 +0530 Subject: [PATCH 09/28] chore: updated readme --- README.md | 22 ++++++++++++++-------- assets/host-switcher.jpeg | Bin 0 -> 69252 bytes 2 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 assets/host-switcher.jpeg diff --git a/README.md b/README.md index 1ed50e9..bf4361f 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,8 @@ Requestly Android SDK lets you debug your android apps without needing you to se - [Features](#features) - [API Debugger](#api-debugger) - [Analytics Event Debugger](#analytics-event-debugger) - - [Logs Debugger](#logs-debugger-coming-soon) + - [Logs Debugger](#logs-debugger) + - [Host Switcher](#host-switcher) - [Acknowledgments](#acknowledgments) ## Installation @@ -31,10 +32,10 @@ RQInterceptor is distributed through [Maven Central](https://search.maven.org/se ``` dependencies { - debugImplementation "io.requestly:requestly-android:2.4.0" - releaseImplementation "io.requestly:requestly-android-noop:2.4.0" - debugImplementation "io.requestly:requestly-android-okhttp:2.4.0" - releaseImplementation "io.requestly:requestly-android-okhttp-noop:2.4.0" + debugImplementation "io.requestly:requestly-android:2.4.2" + releaseImplementation "io.requestly:requestly-android-noop:2.4.2" + debugImplementation "io.requestly:requestly-android-okhttp:2.4.2" + releaseImplementation "io.requestly:requestly-android-okhttp-noop:2.4.2" } ``` @@ -47,12 +48,12 @@ class App : Application(){ super.onCreate() // Initialize Requestly SDK like this - Requestly.Builder(this, "") + Requestly.Builder(this, [optional ""]) .build() } } ``` - +> `sdk-key` is optional. You can use local devtool features without sdk-key.
To get the sdk key, you need to create an app. Follow the steps [here](https://docs.requestly.io/android/tutorial/create-app) to create an app. ## API Debugger Initialization @@ -97,11 +98,16 @@ RequestlyEvent.send(, >) Events Debugger -### Logs Debugger (Coming Soon) +### Logs Debugger Debug your Logs directly from your App. No need to connect your device to your computer to know what's happening inside your app. Logs Debugger +### Host Switcher +Switch between production and staging APIs easily in your Android debug builds. Eg. api.requestly.io → staging.requestly.io + +Logs Debugger + ## Acknowledgments Special Thanks to chuckerteam for maintaining such an awesome project because of which rq-interceptor was possible https://github.com/chuckerteam/chucker diff --git a/assets/host-switcher.jpeg b/assets/host-switcher.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..d312a745236c815e3136ee1a16144fa565d940db GIT binary patch literal 69252 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 >{!@ew?B@e3+TW3NNgnD;`Z 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 literal 0 HcmV?d00001 From 3f6c1c82acc5068a5c58bf3df8af4efc7fdd0ee0 Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Tue, 15 Nov 2022 15:51:19 +0530 Subject: [PATCH 10/28] fix: fix sharedpreferences hilt issue --- gradle.properties | 4 +-- .../internal/support/RequestProcessor.kt | 29 +++++++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index b87d5d1..5d939b3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,9 +19,9 @@ org.gradle.parallel=true android.useAndroidX=true -VERSION_NAME=2.4.2 +VERSION_NAME=2.4.3 # 1*100*100 + 0*100 + 0 => 10000 -VERSION_CODE=20402 +VERSION_CODE=20403 GROUP=io.requestly POM_REPO_NAME=Requestly Android SDK diff --git a/requestly-android-okhttp/src/main/kotlin/io/requestly/android/okhttp/internal/support/RequestProcessor.kt b/requestly-android-okhttp/src/main/kotlin/io/requestly/android/okhttp/internal/support/RequestProcessor.kt index 633c13b..5dd8f2e 100644 --- a/requestly-android-okhttp/src/main/kotlin/io/requestly/android/okhttp/internal/support/RequestProcessor.kt +++ b/requestly-android-okhttp/src/main/kotlin/io/requestly/android/okhttp/internal/support/RequestProcessor.kt @@ -31,8 +31,28 @@ internal class RequestProcessor( ) { private val switchingRulesMap = HashMap() - - init { + private var isStorageListenerInitDone = false + + // NOT WORKING with Hilt +// init { +// val typeToken = object : TypeToken>() {} +// val storageChangeListener: () -> Unit = { +// switchingRulesMap.clear() +// KeyValueStorageManager.getList(HostSwitcherFragmentViewModel.KEY_NAME, typeToken) +// ?.forEach { +// if (it.isActive) { +// switchingRulesMap[it.startingText] = it.provisionalText +// } +// } +// } +// storageChangeListener() +// KeyValueStorageManager.registerChangeListener( +// HostSwitcherFragmentViewModel.KEY_NAME, +// storageChangeListener +// ) +// } + + private fun initStorageListener() { val typeToken = object : TypeToken>() {} val storageChangeListener: () -> Unit = { switchingRulesMap.clear() @@ -48,9 +68,14 @@ internal class RequestProcessor( HostSwitcherFragmentViewModel.KEY_NAME, storageChangeListener ) + isStorageListenerInitDone = true } fun process(req: Request, transaction: HttpTransaction): Request { + if(!isStorageListenerInitDone) { + initStorageListener() + } + var urlString = req.url.toString() switchingRulesMap.forEach { urlString = urlString.replace(it.key, it.value, ignoreCase = true) From 852194e1c71d2a3f91b07f8507c5b9459bd6a120 Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Tue, 15 Nov 2022 16:05:58 +0530 Subject: [PATCH 11/28] chore: update readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bf4361f..ad6efeb 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,10 @@ RQInterceptor is distributed through [Maven Central](https://search.maven.org/se ``` dependencies { - debugImplementation "io.requestly:requestly-android:2.4.2" - releaseImplementation "io.requestly:requestly-android-noop:2.4.2" - debugImplementation "io.requestly:requestly-android-okhttp:2.4.2" - releaseImplementation "io.requestly:requestly-android-okhttp-noop:2.4.2" + debugImplementation "io.requestly:requestly-android:2.4.3" + releaseImplementation "io.requestly:requestly-android-noop:2.4.3" + debugImplementation "io.requestly:requestly-android-okhttp:2.4.3" + releaseImplementation "io.requestly:requestly-android-okhttp-noop:2.4.3" } ``` From e5dc9dbb65808ec1d32f15e5d1caf0e9e4f00ebd Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Tue, 15 Nov 2022 16:14:49 +0530 Subject: [PATCH 12/28] chore: added release pipelines --- .github/workflows/publish-release.yaml | 31 +++++++++++++++++++++++++ .github/workflows/publish-snapshot.yaml | 31 +++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 .github/workflows/publish-release.yaml create mode 100644 .github/workflows/publish-snapshot.yaml diff --git a/.github/workflows/publish-release.yaml b/.github/workflows/publish-release.yaml new file mode 100644 index 0000000..89fc861 --- /dev/null +++ b/.github/workflows/publish-release.yaml @@ -0,0 +1,31 @@ +name: Publish Release +on: workflow_dispatch + +jobs: + publish: + if: ${{ github.repository == 'requestly/requestly-android-sdk'}} + runs-on: [ubuntu-latest] + + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + + - name: Publish to Maven Local + run: ./gradlew publishToMavenLocal + env: + ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} + ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} + + - name: Upload Build Artifacts + uses: actions/upload-artifact@v3 + with: + name: 'requestly-android-sdk-release-artifacts' + path: '~/.m2/repository/' + + - name: Publish to the Staging Repository + run: ./gradlew publishReleasePublicationToStagingRepository --no-parallel + env: + ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} + ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} + ORG_GRADLE_PROJECT_NEXUS_USERNAME: ${{ secrets.ORG_GRADLE_PROJECT_NEXUS_USERNAME }} + ORG_GRADLE_PROJECT_NEXUS_PASSWORD: ${{ secrets.ORG_GRADLE_PROJECT_NEXUS_PASSWORD }} \ No newline at end of file diff --git a/.github/workflows/publish-snapshot.yaml b/.github/workflows/publish-snapshot.yaml new file mode 100644 index 0000000..8603732 --- /dev/null +++ b/.github/workflows/publish-snapshot.yaml @@ -0,0 +1,31 @@ +name: Publish Snapshot +on: workflow_dispatch + +jobs: + publish: + if: ${{ github.repository == 'requestly/requestly-android-sdk'}} + runs-on: [ubuntu-latest] + + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + + - name: Publish to Maven Local + run: ./gradlew publishToMavenLocal + env: + ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} + ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} + + - name: Upload Build Artifacts + uses: actions/upload-artifact@v3 + with: + name: 'requestly-android-sdk-snapshot-artifacts' + path: '~/.m2/repository/' + + - name: Publish to the Staging Repository + run: ./gradlew publishReleasePublicationToSnapshotRepository --no-parallel + env: + ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} + ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} + ORG_GRADLE_PROJECT_NEXUS_USERNAME: ${{ secrets.ORG_GRADLE_PROJECT_NEXUS_USERNAME }} + ORG_GRADLE_PROJECT_NEXUS_PASSWORD: ${{ secrets.ORG_GRADLE_PROJECT_NEXUS_PASSWORD }} \ No newline at end of file From bf9e16b6e72d83eb9547893e052b4d5691565981 Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Wed, 16 Nov 2022 17:51:45 +0530 Subject: [PATCH 13/28] chore: added cryptodemo as try now --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index ad6efeb..9035dde 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Requestly Android SDK lets you debug your android apps without needing you to se - [Analytics Event Debugger](#analytics-event-debugger) - [Logs Debugger](#logs-debugger) - [Host Switcher](#host-switcher) +- [Try Now](#try-now) - [Acknowledgments](#acknowledgments) ## Installation @@ -108,6 +109,10 @@ Switch between production and staging APIs easily in your Android debug builds. Logs Debugger +## Try Now + +Try Now + ## Acknowledgments Special Thanks to chuckerteam for maintaining such an awesome project because of which rq-interceptor was possible https://github.com/chuckerteam/chucker From 50da3c85251531953dffcd1bdfbb50da60634485 Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Wed, 16 Nov 2022 18:27:26 +0530 Subject: [PATCH 14/28] chore: update playground link to open in new tab --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9035dde..d43fd37 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ Switch between production and staging APIs easily in your Android debug builds. ## Try Now -Try Now +Try Now ## Acknowledgments Special Thanks to chuckerteam for maintaining such an awesome project because of which rq-interceptor was possible From da6883fe545f36472b118181792fe0a06b868556 Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Thu, 17 Nov 2022 13:05:16 +0530 Subject: [PATCH 15/28] fix: added sourceCompatibility and targetCompatibility --- requestly-android-core-noop/build.gradle | 6 ++++++ requestly-android-noop/build.gradle | 8 ++++++++ requestly-android-okhttp-noop/build.gradle | 6 ++++++ requestly-android-okhttp/build.gradle | 2 ++ requestly-android/build.gradle | 8 ++++++++ 5 files changed, 30 insertions(+) diff --git a/requestly-android-core-noop/build.gradle b/requestly-android-core-noop/build.gradle index 39b9071..206e40d 100644 --- a/requestly-android-core-noop/build.gradle +++ b/requestly-android-core-noop/build.gradle @@ -11,6 +11,12 @@ android { '-module-name', "io.requestly.android.core", "-Xexplicit-api=strict" ] + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' } buildFeatures { diff --git a/requestly-android-noop/build.gradle b/requestly-android-noop/build.gradle index 45aec10..22e7dce 100644 --- a/requestly-android-noop/build.gradle +++ b/requestly-android-noop/build.gradle @@ -10,6 +10,14 @@ android { minSdk rootProject.minSdkVersion targetSdk rootProject.targetSdkVersion } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } } dependencies { diff --git a/requestly-android-okhttp-noop/build.gradle b/requestly-android-okhttp-noop/build.gradle index b0daebb..7dcb224 100644 --- a/requestly-android-okhttp-noop/build.gradle +++ b/requestly-android-okhttp-noop/build.gradle @@ -9,6 +9,12 @@ android { '-module-name', "io.requestly.android.okhttp", "-Xexplicit-api=strict" ] + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" } buildFeatures { diff --git a/requestly-android-okhttp/build.gradle b/requestly-android-okhttp/build.gradle index 5309cea..9129f6a 100644 --- a/requestly-android-okhttp/build.gradle +++ b/requestly-android-okhttp/build.gradle @@ -11,6 +11,8 @@ android { '-module-name', "io.requestly.android.okhttp", "-Xexplicit-api=strict" ] + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } defaultConfig { diff --git a/requestly-android/build.gradle b/requestly-android/build.gradle index 5bd5896..33faaa5 100644 --- a/requestly-android/build.gradle +++ b/requestly-android/build.gradle @@ -10,6 +10,14 @@ android { minSdk rootProject.minSdkVersion targetSdk rootProject.targetSdkVersion } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } } dependencies { From f4551830260ad1222cd9b80506c7319167ecdbfd Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Thu, 17 Nov 2022 13:06:14 +0530 Subject: [PATCH 16/28] chore: bump version to 2.4.4 --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 5d939b3..bb4aa5b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,9 +19,9 @@ org.gradle.parallel=true android.useAndroidX=true -VERSION_NAME=2.4.3 +VERSION_NAME=2.4.4 # 1*100*100 + 0*100 + 0 => 10000 -VERSION_CODE=20403 +VERSION_CODE=20404 GROUP=io.requestly POM_REPO_NAME=Requestly Android SDK From 8eb864c496a926f8b4988264b297e9b9e195ce6e Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Thu, 17 Nov 2022 17:26:23 +0530 Subject: [PATCH 17/28] fix: main_nav_graph collision fix --- .../src/main/res/layout/activity_main_requestly.xml | 2 +- .../{main_nav_graph.xml => rq_main_nav_graph.xml} | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename requestly-android-core/src/main/res/navigation/{main_nav_graph.xml => rq_main_nav_graph.xml} (82%) diff --git a/requestly-android-core/src/main/res/layout/activity_main_requestly.xml b/requestly-android-core/src/main/res/layout/activity_main_requestly.xml index ea453bd..fc589ca 100644 --- a/requestly-android-core/src/main/res/layout/activity_main_requestly.xml +++ b/requestly-android-core/src/main/res/layout/activity_main_requestly.xml @@ -14,7 +14,7 @@ app:defaultNavHost="true" app:layout_constraintBottom_toTopOf="@id/updateNotifyStripView" app:layout_constraintTop_toTopOf="parent" - app:navGraph="@navigation/main_nav_graph" /> + app:navGraph="@navigation/rq_main_nav_graph" /> @@ -13,16 +13,16 @@ + app:popUpTo="@id/rq_main_nav_graph" /> + app:popUpTo="@id/rq_main_nav_graph" /> + app:popUpTo="@id/rq_main_nav_graph" /> From ad0f49c812500c4e9eb1a242d0f607566d057cf0 Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Thu, 17 Nov 2022 17:26:52 +0530 Subject: [PATCH 18/28] chore: bump version to 2.4.5 --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index bb4aa5b..b94d7d1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,9 +19,9 @@ org.gradle.parallel=true android.useAndroidX=true -VERSION_NAME=2.4.4 +VERSION_NAME=2.4.5 # 1*100*100 + 0*100 + 0 => 10000 -VERSION_CODE=20404 +VERSION_CODE=20405 GROUP=io.requestly POM_REPO_NAME=Requestly Android SDK From 3ed1540560ee4db1599c8448ec45ecd13d90641f Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Thu, 17 Nov 2022 17:32:04 +0530 Subject: [PATCH 19/28] fix: fix import error --- .../io/requestly/android/core/navigation/Navigator.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/navigation/Navigator.kt b/requestly-android-core/src/main/java/io/requestly/android/core/navigation/Navigator.kt index 96f44b0..58a1d32 100644 --- a/requestly-android-core/src/main/java/io/requestly/android/core/navigation/Navigator.kt +++ b/requestly-android-core/src/main/java/io/requestly/android/core/navigation/Navigator.kt @@ -1,7 +1,7 @@ package io.requestly.android.core.navigation import androidx.navigation.NavController -import io.requestly.android.core.MainNavGraphDirections +import io.requestly.android.core.RqMainNavGraphDirections class Navigator { lateinit var navController: NavController @@ -9,16 +9,16 @@ class Navigator { // navigate on main thread or nav component crashes sometimes fun navigateToFlow(navigationFlow: NavigationFlow) = when (navigationFlow) { is NavigationFlow.NetworkFlow -> navController.navigate( - MainNavGraphDirections.actionGlobalNetworkFlow() + RqMainNavGraphDirections.actionGlobalNetworkFlow() ) is NavigationFlow.AnalyticsFlow -> navController.navigate( - MainNavGraphDirections.actionGlobalAnalyticsFlow() + RqMainNavGraphDirections.actionGlobalAnalyticsFlow() ) is NavigationFlow.HostSwitcherFlow -> navController.navigate( - MainNavGraphDirections.actionGlobalHostSwitcher() + RqMainNavGraphDirections.actionGlobalHostSwitcher() ) // is NavigationFlow.DashboardFlow -> navController.navigate( -// MainNavGraphDirections.actionGlobalDashboardFlow(navigationFlow.msg) +// RqMainNavGraphDirections.actionGlobalDashboardFlow(navigationFlow.msg) // ) } } From 9449f4ad508da914918fbfce93801d01e561cb4e Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Mon, 21 Nov 2022 12:23:10 +0530 Subject: [PATCH 20/28] chore: update readme --- README.md | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d43fd37..f90844d 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Requestly Android SDK lets you debug your android apps without needing you to setup any proxies or install any certificates everytime. It makes easy to identify & debug your Android Apps faster and save your time. +- [Try Now](#try-now) - [Installation](#installation) - [SDK Initialization](#sdk-initialization) - [API Debugger Initialization](#api-debugger-initialization) @@ -23,8 +24,24 @@ Requestly Android SDK lets you debug your android apps without needing you to se - [Analytics Event Debugger](#analytics-event-debugger) - [Logs Debugger](#logs-debugger) - [Host Switcher](#host-switcher) -- [Try Now](#try-now) - [Acknowledgments](#acknowledgments) + +## Try Now +### ↓ Click on Screenshots to try Apps ↓ + + Try Now + + + Try Now + + + Try Now + + +### OR +- [Dowload CryptoDemo Apk](https://github.com/requestly/android-debugger-examples/releases/latest/download/cryptodemo-debug.apk) +- [Download Infinity Reddit Apk](https://github.com/requestly/android-debugger-examples/releases/latest/download/infinity-reddit-debug.apk) +- [Download Pokedex Apk](https://github.com/requestly/android-debugger-examples/releases/latest/download/pokedex-debug.apk) ## Installation The best way to install the Requestly Android SDK is with a build system like Gradle. This ensures you can easily upgrade to the latest versions. @@ -109,10 +126,6 @@ Switch between production and staging APIs easily in your Android debug builds. Logs Debugger -## Try Now - -Try Now - ## Acknowledgments Special Thanks to chuckerteam for maintaining such an awesome project because of which rq-interceptor was possible https://github.com/chuckerteam/chucker From 57dc59a326609bc6f76de706c88792d38ae200c9 Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Mon, 21 Nov 2022 12:36:27 +0530 Subject: [PATCH 21/28] chore: update readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f90844d..618801f 100644 --- a/README.md +++ b/README.md @@ -29,13 +29,13 @@ Requestly Android SDK lets you debug your android apps without needing you to se ## Try Now ### ↓ Click on Screenshots to try Apps ↓ - Try Now + Try Now - Try Now + Try Now - Try Now + Try Now ### OR From 63c91c212a3b0577fa9d0c6554f1faf56110e696 Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Mon, 21 Nov 2022 13:12:18 +0530 Subject: [PATCH 22/28] chore: update badges in readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 618801f..ca00ad6 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@

GitHub release Maven + Maven

From 9b268b6573ab372108ac7f14a3248b6f24195f12 Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Mon, 21 Nov 2022 13:13:05 +0530 Subject: [PATCH 23/28] chore: updated maven badge in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ca00ad6..7990cb9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

GitHub release - Maven + Maven Maven

From 4f54ec2feff62c14d5737fa49fc450b19c022530 Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Mon, 21 Nov 2022 14:06:14 +0530 Subject: [PATCH 24/28] fix: move out synchrnous code while init --- .../java/io/requestly/android/core/Requestly.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/Requestly.kt b/requestly-android-core/src/main/java/io/requestly/android/core/Requestly.kt index 0a4e58b..979a1be 100644 --- a/requestly-android-core/src/main/java/io/requestly/android/core/Requestly.kt +++ b/requestly-android-core/src/main/java/io/requestly/android/core/Requestly.kt @@ -51,17 +51,17 @@ class Requestly { // End Configuration fun build() { - applicationScope.launch { - Log.d(Constants.LOG_TAG, "Start: Building Core") + Log.d(Constants.LOG_TAG, "Start: Building Core") - // Create Requestly Instance - INSTANCE = Requestly() - getInstance().applicationContext = applicationContext - getInstance().listNotificationHelper = ListNotificationHelper(applicationContext) + // Create Requestly Instance + INSTANCE = Requestly() + getInstance().applicationContext = applicationContext + getInstance().listNotificationHelper = ListNotificationHelper(applicationContext) - // Init KeyValueStorageManager - KeyValueStorageManager.initialize(applicationContext) + // Init KeyValueStorageManager + KeyValueStorageManager.initialize(applicationContext) + applicationScope.launch { // Init SettingsManager Singleton // Overrides the token set previously appToken?.let { From 869908cd198c954a822f7639a4cd0c9aab746896 Mon Sep 17 00:00:00 2001 From: Sahil Gupta Date: Mon, 21 Nov 2022 14:54:18 +0530 Subject: [PATCH 25/28] chore: update readme --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 7990cb9..9ab3136 100644 --- a/README.md +++ b/README.md @@ -128,5 +128,4 @@ Switch between production and staging APIs easily in your Android debug builds. Logs Debugger ## Acknowledgments -Special Thanks to chuckerteam for maintaining such an awesome project because of which rq-interceptor was possible -https://github.com/chuckerteam/chucker +Special Thanks to [chuckerteam](https://github.com/chuckerteam/chucker) for maintaining such an awesome project [Chucker](https://github.com/chuckerteam/chucker) because of which requestly-android-sdk was possible. From d61bd1e9f95ab7cefa937a6178fb6cf95fa394bc Mon Sep 17 00:00:00 2001 From: P1V4HH2CFG Date: Mon, 21 Nov 2022 15:54:36 +0530 Subject: [PATCH 26/28] snapshot testing --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index b94d7d1..655dfb7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,7 +19,7 @@ org.gradle.parallel=true android.useAndroidX=true -VERSION_NAME=2.4.5 +VERSION_NAME=2.4.5-SNAPSHOT # 1*100*100 + 0*100 + 0 => 10000 VERSION_CODE=20405 GROUP=io.requestly From 700a3fadc4823264c6373b92a6f73b3439ee3515 Mon Sep 17 00:00:00 2001 From: P1V4HH2CFG Date: Thu, 1 Dec 2022 13:32:33 +0530 Subject: [PATCH 27/28] merge conflicts removed --- .../core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt index 1dc0448..1576cae 100644 --- a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt +++ b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt @@ -23,6 +23,7 @@ class SharedPrefViewerFragment : Fragment() { private lateinit var mainBinding: FragmentSharedPrefViewerBinding + private val viewModel: SharedPrefViewerViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { From e50a7499e308c11764e919ef0abc56ea67d2dc83 Mon Sep 17 00:00:00 2001 From: P1V4HH2CFG Date: Thu, 1 Dec 2022 13:36:24 +0530 Subject: [PATCH 28/28] unused code removed --- .../core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt index 1576cae..1dc0448 100644 --- a/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt +++ b/requestly-android-core/src/main/java/io/requestly/android/core/modules/sharedPrefViewer/SharedPrefViewerFragment.kt @@ -23,7 +23,6 @@ class SharedPrefViewerFragment : Fragment() { private lateinit var mainBinding: FragmentSharedPrefViewerBinding - private val viewModel: SharedPrefViewerViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) {