mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	1.2.2
This commit is contained in:
		
							parent
							
								
									5e619c18cc
								
							
						
					
					
						commit
						2839d59a28
					
				
					 11 changed files with 174 additions and 169 deletions
				
			
		|  | @ -1,87 +0,0 @@ | |||
| package com.lagradost.cloudstream3 | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.APIHolder.apis | ||||
| import com.lagradost.cloudstream3.mvvm.normalSafeApiCall | ||||
| import com.lagradost.cloudstream3.mvvm.safeApiCall | ||||
| 
 | ||||
| class AllProvider : MainAPI() { | ||||
|     override val name: String | ||||
|         get() = "All Sources" | ||||
| 
 | ||||
|     var providersActive = HashSet<String>() | ||||
| 
 | ||||
|     override fun quickSearch(query: String): ArrayList<SearchResponse>? { | ||||
|         val list = apis.filter { a -> | ||||
|             a.name != this.name && (providersActive.size == 0 || providersActive.contains(a.name)) | ||||
|         }.filter { a -> a.hasQuickSearch }.pmap { a -> | ||||
|             normalSafeApiCall { | ||||
|                 a.quickSearch(query) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         var maxCount = 0 | ||||
|         var providerCount = 0 | ||||
|         for (res in list) { | ||||
|             if (res != null) { | ||||
|                 if (res.size > maxCount) { | ||||
|                     maxCount = res.size | ||||
|                 } | ||||
|                 providerCount++ | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         println("PROV: " + providerCount + "|" + maxCount) | ||||
|         if (providerCount == 0) return null | ||||
|         if (maxCount == 0) return ArrayList() | ||||
| 
 | ||||
|         val result = ArrayList<SearchResponse>() | ||||
|         for (i in 0..maxCount) { | ||||
|             for (res in list) { | ||||
|                 if (res != null) { | ||||
|                     if (i < res.size) { | ||||
|                         result.add(res[i]) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return result | ||||
|     } | ||||
| 
 | ||||
|     override fun search(query: String): ArrayList<SearchResponse>? { | ||||
|         val list = apis.filter { a -> | ||||
|             a.name != this.name && (providersActive.size == 0 || providersActive.contains(a.name)) | ||||
|         }.pmap { a -> | ||||
|             normalSafeApiCall { | ||||
|                 a.search(query) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         var maxCount = 0 | ||||
|         var providerCount = 0 | ||||
|         for (res in list) { | ||||
|             if (res != null) { | ||||
|                 if (res.size > maxCount) { | ||||
|                     maxCount = res.size | ||||
|                 } | ||||
|                 providerCount++ | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (providerCount == 0) return null | ||||
|         if (maxCount == 0) return ArrayList() | ||||
| 
 | ||||
|         val result = ArrayList<SearchResponse>() | ||||
|         for (i in 0..maxCount) { | ||||
|             for (res in list) { | ||||
|                 if (res != null) { | ||||
|                     if (i < res.size) { | ||||
|                         result.add(res[i]) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return result | ||||
|     } | ||||
| } | ||||
|  | @ -25,8 +25,6 @@ object APIHolder { | |||
|     val unixTime: Long | ||||
|         get() = System.currentTimeMillis() / 1000L | ||||
| 
 | ||||
|     val allApi = AllProvider() | ||||
| 
 | ||||
|     private const val defProvider = 0 | ||||
| 
 | ||||
|     val apis = arrayListOf( | ||||
|  |  | |||
|  | @ -7,8 +7,13 @@ import com.lagradost.cloudstream3.mvvm.safeApiCall | |||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| 
 | ||||
| class APIRepository(val api: MainAPI) { | ||||
|     companion object { | ||||
|         var providersActive = HashSet<String>() | ||||
|     } | ||||
| 
 | ||||
|     val name: String get() = api.name | ||||
|     val mainUrl: String get() = api.mainUrl | ||||
|     val hasQuickSearch: Boolean  get() = api.hasQuickSearch | ||||
| 
 | ||||
|     suspend fun load(url: String): Resource<LoadResponse> { | ||||
|         return safeApiCall { | ||||
|  |  | |||
|  | @ -1,6 +1,8 @@ | |||
| package com.lagradost.cloudstream3.ui.home | ||||
| 
 | ||||
| import android.annotation.SuppressLint | ||||
| import android.app.Activity | ||||
| import android.content.Context | ||||
| import android.content.Intent | ||||
| import android.content.res.Configuration | ||||
| import android.net.Uri | ||||
|  | @ -45,6 +47,50 @@ import kotlinx.android.synthetic.main.fragment_home.* | |||
| const val HOME_BOOKMARK_VALUE = "home_bookmarked_last" | ||||
| 
 | ||||
| class HomeFragment : Fragment() { | ||||
|     companion object { | ||||
|         val configEvent = Event<Int>() | ||||
|         var currentSpan = 1 | ||||
| 
 | ||||
|         fun Activity.loadHomepageList(item: HomePageList) { | ||||
|             val context = this | ||||
|             val bottomSheetDialogBuilder = BottomSheetDialog(context) | ||||
|             bottomSheetDialogBuilder.setContentView(R.layout.home_episodes_expanded) | ||||
|             val title = bottomSheetDialogBuilder.findViewById<TextView>(R.id.home_expanded_text)!! | ||||
|             title.text = item.name | ||||
|             val recycle = bottomSheetDialogBuilder.findViewById<AutofitRecyclerView>(R.id.home_expanded_recycler)!! | ||||
|             val titleHolder = bottomSheetDialogBuilder.findViewById<FrameLayout>(R.id.home_expanded_drag_down)!! | ||||
| 
 | ||||
|             titleHolder.setOnClickListener { | ||||
|                 bottomSheetDialogBuilder.dismiss() | ||||
|             } | ||||
| 
 | ||||
|             // Span settings | ||||
|             recycle.spanCount = currentSpan | ||||
| 
 | ||||
|             recycle.adapter = SearchAdapter(item.list, recycle) { callback -> | ||||
|                 handleSearchClickCallback(this, callback) | ||||
|                 if (callback.action == SEARCH_ACTION_LOAD) { | ||||
|                     bottomSheetDialogBuilder.dismiss() | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             val spanListener = { span: Int -> | ||||
|                 recycle.spanCount = span | ||||
|                 (recycle.adapter as SearchAdapter).notifyDataSetChanged() | ||||
|             } | ||||
| 
 | ||||
|             configEvent += spanListener | ||||
| 
 | ||||
|             bottomSheetDialogBuilder.setOnDismissListener { | ||||
|                 configEvent -= spanListener | ||||
|             } | ||||
| 
 | ||||
|             (recycle.adapter as SearchAdapter).notifyDataSetChanged() | ||||
| 
 | ||||
|             bottomSheetDialogBuilder.show() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private lateinit var homeViewModel: HomeViewModel | ||||
| 
 | ||||
|     override fun onCreateView( | ||||
|  | @ -58,8 +104,6 @@ class HomeFragment : Fragment() { | |||
|         return inflater.inflate(R.layout.fragment_home, container, false) | ||||
|     } | ||||
| 
 | ||||
|     private val configEvent = Event<Int>() | ||||
|     private var currentSpan = 1 | ||||
|     private var currentHomePage: HomePageResponse? = null | ||||
|     var currentMainIndex = 0 | ||||
|     var currentMainList: ArrayList<SearchResponse> = ArrayList() | ||||
|  | @ -254,48 +298,11 @@ class HomeFragment : Fragment() { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         fun loadHomepageList(item: HomePageList) { | ||||
|             val bottomSheetDialogBuilder = BottomSheetDialog(view.context) | ||||
|             bottomSheetDialogBuilder.setContentView(R.layout.home_episodes_expanded) | ||||
|             val title = bottomSheetDialogBuilder.findViewById<TextView>(R.id.home_expanded_text)!! | ||||
|             title.text = item.name | ||||
|             val recycle = bottomSheetDialogBuilder.findViewById<AutofitRecyclerView>(R.id.home_expanded_recycler)!! | ||||
|             val titleHolder = bottomSheetDialogBuilder.findViewById<FrameLayout>(R.id.home_expanded_drag_down)!! | ||||
| 
 | ||||
|             titleHolder.setOnClickListener { | ||||
|                 bottomSheetDialogBuilder.dismiss() | ||||
|             } | ||||
| 
 | ||||
|             // Span settings | ||||
|             recycle.spanCount = currentSpan | ||||
| 
 | ||||
|             recycle.adapter = SearchAdapter(item.list, recycle) { callback -> | ||||
|                 handleSearchClickCallback(activity, callback) | ||||
|                 if (callback.action == SEARCH_ACTION_LOAD) { | ||||
|                     bottomSheetDialogBuilder.dismiss() | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             val spanListener = { span: Int -> | ||||
|                 recycle.spanCount = span | ||||
|                 (recycle.adapter as SearchAdapter).notifyDataSetChanged() | ||||
|             } | ||||
| 
 | ||||
|             configEvent += spanListener | ||||
| 
 | ||||
|             bottomSheetDialogBuilder.setOnDismissListener { | ||||
|                 configEvent -= spanListener | ||||
|             } | ||||
| 
 | ||||
|             (recycle.adapter as SearchAdapter).notifyDataSetChanged() | ||||
| 
 | ||||
|             bottomSheetDialogBuilder.show() | ||||
|         } | ||||
| 
 | ||||
|         val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = ParentItemAdapter(listOf(), { callback -> | ||||
|             handleSearchClickCallback(activity, callback) | ||||
|         }, { item -> | ||||
|             loadHomepageList(item) | ||||
|             activity?.loadHomepageList(item) | ||||
|         }) | ||||
| 
 | ||||
|         observe(homeViewModel.availableWatchStatusTypes) { availableWatchStatusTypes -> | ||||
|  | @ -319,7 +326,7 @@ class HomeFragment : Fragment() { | |||
|             home_bookmarked_child_recyclerview?.adapter?.notifyDataSetChanged() | ||||
| 
 | ||||
|             home_bookmarked_child_more_info.setOnClickListener { | ||||
|                 loadHomepageList( | ||||
|                 activity?.loadHomepageList( | ||||
|                     HomePageList( | ||||
|                         home_bookmarked_parent_item_title?.text?.toString() ?: getString(R.string.error_bookmarks_text), | ||||
|                         bookmarks | ||||
|  |  | |||
|  | @ -14,16 +14,20 @@ import androidx.appcompat.widget.SearchView | |||
| import androidx.fragment.app.Fragment | ||||
| import androidx.lifecycle.ViewModelProvider | ||||
| import androidx.preference.PreferenceManager | ||||
| import androidx.recyclerview.widget.GridLayoutManager | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import com.lagradost.cloudstream3.APIHolder.allApi | ||||
| import com.lagradost.cloudstream3.APIHolder.apis | ||||
| import com.lagradost.cloudstream3.APIHolder.getApiSettings | ||||
| import com.lagradost.cloudstream3.HomePageList | ||||
| import com.lagradost.cloudstream3.R | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.getGridIsCompact | ||||
| import com.lagradost.cloudstream3.mvvm.Resource | ||||
| import com.lagradost.cloudstream3.mvvm.observe | ||||
| import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment | ||||
| import com.lagradost.cloudstream3.ui.APIRepository.Companion.providersActive | ||||
| import com.lagradost.cloudstream3.ui.home.HomeFragment | ||||
| import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageList | ||||
| import com.lagradost.cloudstream3.ui.home.ParentItemAdapter | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.getGridIsCompact | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard | ||||
| import kotlinx.android.synthetic.main.fragment_search.* | ||||
| 
 | ||||
|  | @ -49,11 +53,14 @@ class SearchFragment : Fragment() { | |||
|         val spanCountPortrait = if (compactView) 1 else 3 | ||||
|         val orientation = resources.configuration.orientation | ||||
| 
 | ||||
|         if (orientation == Configuration.ORIENTATION_LANDSCAPE) { | ||||
|             cardSpace.spanCount = spanCountLandscape | ||||
|         val currentSpan = if (orientation == Configuration.ORIENTATION_LANDSCAPE) { | ||||
|             spanCountLandscape | ||||
|         } else { | ||||
|             cardSpace.spanCount = spanCountPortrait | ||||
|             spanCountPortrait | ||||
|         } | ||||
|         cardSpace.spanCount = currentSpan | ||||
|         HomeFragment.currentSpan = currentSpan | ||||
|         HomeFragment.configEvent.invoke(currentSpan) | ||||
|     } | ||||
| 
 | ||||
|     override fun onConfigurationChanged(newConfig: Configuration) { | ||||
|  | @ -113,7 +120,7 @@ class SearchFragment : Fragment() { | |||
|                             apiNames.filter { a -> apiNamesSettingLocal.contains(a) }.toSet() | ||||
|                         ) | ||||
|                         edit.apply() | ||||
|                         allApi.providersActive = apiNamesSettingLocal | ||||
|                         providersActive = apiNamesSettingLocal | ||||
|                     } | ||||
|                 } | ||||
|                 builder.setTitle("Search Providers") | ||||
|  | @ -138,9 +145,11 @@ class SearchFragment : Fragment() { | |||
|             when (it) { | ||||
|                 is Resource.Success -> { | ||||
|                     it.value.let { data -> | ||||
|                         if (data != null) { | ||||
|                             (cardSpace?.adapter as SearchAdapter?)?.cardList = data.filterNotNull() | ||||
|                             cardSpace?.adapter?.notifyDataSetChanged() | ||||
|                         if (data.isNotEmpty()) { | ||||
|                             (cardSpace?.adapter as SearchAdapter?)?.apply { | ||||
|                                 cardList = data.toList() | ||||
|                                 notifyDataSetChanged() | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     searchExitIcon.alpha = 1f | ||||
|  | @ -157,8 +166,18 @@ class SearchFragment : Fragment() { | |||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         observe(searchViewModel.currentSearch) { list -> | ||||
|             (search_master_recycler?.adapter as ParentItemAdapter?)?.apply { | ||||
|                 items = list.map { | ||||
|                     HomePageList(it.apiName, if (it.data is Resource.Success) it.data.value else ArrayList()) | ||||
|                 } | ||||
|                 notifyDataSetChanged() | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         activity?.let { | ||||
|             allApi.providersActive = it.getApiSettings() | ||||
|             providersActive = it.getApiSettings() | ||||
|         } | ||||
| 
 | ||||
|         main_search.setOnQueryTextFocusChangeListener { searchView, b -> | ||||
|  | @ -173,6 +192,22 @@ class SearchFragment : Fragment() { | |||
|             } | ||||
|         } | ||||
|         main_search.onActionViewExpanded() | ||||
| 
 | ||||
|         val masterAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = ParentItemAdapter(listOf(), { callback -> | ||||
|             SearchHelper.handleSearchClickCallback(activity, callback) | ||||
|         }, { item -> | ||||
|             activity?.loadHomepageList(item) | ||||
|         }) | ||||
| 
 | ||||
|         search_master_recycler.adapter = masterAdapter | ||||
|         search_master_recycler.layoutManager = GridLayoutManager(context, 1) | ||||
| 
 | ||||
|         val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) | ||||
|         val isAdvancedSearch = settingsManager.getBoolean("advanced_search", true) | ||||
| 
 | ||||
|         search_master_recycler.visibility = if(isAdvancedSearch) View.VISIBLE else View.GONE | ||||
|         cardSpace.visibility = if(!isAdvancedSearch) View.VISIBLE else View.GONE | ||||
| 
 | ||||
|         // SubtitlesFragment.push(activity) | ||||
|         //searchViewModel.search("iron man") | ||||
|         //(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro") | ||||
|  |  | |||
|  | @ -4,18 +4,27 @@ import androidx.lifecycle.LiveData | |||
| import androidx.lifecycle.MutableLiveData | ||||
| import androidx.lifecycle.ViewModel | ||||
| import androidx.lifecycle.viewModelScope | ||||
| import com.lagradost.cloudstream3.APIHolder.allApi | ||||
| import com.lagradost.cloudstream3.APIHolder.apis | ||||
| import com.lagradost.cloudstream3.SearchResponse | ||||
| import com.lagradost.cloudstream3.mvvm.Resource | ||||
| import com.lagradost.cloudstream3.mvvm.safeApiCall | ||||
| import com.lagradost.cloudstream3.ui.APIRepository | ||||
| import com.lagradost.cloudstream3.ui.APIRepository.Companion.providersActive | ||||
| import kotlinx.coroutines.launch | ||||
| 
 | ||||
| data class OnGoingSearch( | ||||
|     val apiName: String, | ||||
|     val data: Resource<ArrayList<SearchResponse>> | ||||
| ) | ||||
| 
 | ||||
| class SearchViewModel : ViewModel() { | ||||
|     private val _searchResponse: MutableLiveData<Resource<ArrayList<SearchResponse>>> = MutableLiveData() | ||||
|     val searchResponse: LiveData<Resource<ArrayList<SearchResponse>>> get() = _searchResponse | ||||
| 
 | ||||
|     private val _currentSearch: MutableLiveData<ArrayList<OnGoingSearch>> = MutableLiveData() | ||||
|     val currentSearch: LiveData<ArrayList<OnGoingSearch>> get() = _currentSearch | ||||
| 
 | ||||
|     var searchCounter = 0 | ||||
|     private val repo = APIRepository(allApi) | ||||
|     private val repos = apis.map { APIRepository(it) } | ||||
| 
 | ||||
|     private fun clearSearch() { | ||||
|         _searchResponse.postValue(Resource.Success(ArrayList())) | ||||
|  | @ -23,29 +32,50 @@ class SearchViewModel : ViewModel() { | |||
| 
 | ||||
|     fun search(query: String) = viewModelScope.launch { | ||||
|         searchCounter++ | ||||
|         if(query.length <= 1) { | ||||
|         if (query.length <= 1) { | ||||
|             clearSearch() | ||||
|             return@launch | ||||
|         } | ||||
|         val localSearchCounter = searchCounter | ||||
|         _searchResponse.postValue(Resource.Loading()) | ||||
|         val data = repo.search(query) | ||||
| 
 | ||||
|         if(localSearchCounter != searchCounter) return@launch | ||||
|         _searchResponse.postValue(data) | ||||
|         val currentList = ArrayList<OnGoingSearch>() | ||||
| 
 | ||||
|         _currentSearch.postValue(ArrayList()) | ||||
| 
 | ||||
|         repos.filter { a -> | ||||
|             (providersActive.size == 0 || providersActive.contains(a.name)) | ||||
|         }.map { a -> | ||||
|             currentList.add(OnGoingSearch(a.name, a.search(query))) | ||||
|             if (localSearchCounter == searchCounter) { | ||||
|                 _currentSearch.postValue(currentList) | ||||
|             } | ||||
|         } | ||||
|         _currentSearch.postValue(currentList) | ||||
| 
 | ||||
|         if (localSearchCounter != searchCounter) return@launch | ||||
| 
 | ||||
|         val list = ArrayList<SearchResponse>() | ||||
|         val nestedList = currentList.map { it.data }.filterIsInstance<Resource.Success<ArrayList<SearchResponse>>>().map { it.value } | ||||
| 
 | ||||
|         // I do it this way to move the relevant search results to the top | ||||
|         var index = 0 | ||||
|         while (true) { | ||||
|             var added = 0 | ||||
|             for (sublist in nestedList) { | ||||
|                 if(sublist.size > index) { | ||||
|                     list.add(sublist[index]) | ||||
|                     added++ | ||||
|                 } | ||||
|             } | ||||
|             if(added == 0) break | ||||
|             index++ | ||||
|         } | ||||
| 
 | ||||
|         _searchResponse.postValue(Resource.Success(list)) | ||||
|     } | ||||
| 
 | ||||
|     fun quickSearch(query: String) = viewModelScope.launch { | ||||
|         searchCounter++ | ||||
|         if(query.length <= 1) { | ||||
|             clearSearch() | ||||
|             return@launch | ||||
|         } | ||||
|         val localSearchCounter = searchCounter | ||||
|         _searchResponse.postValue(Resource.Loading()) | ||||
|         val data = repo.quickSearch(query) | ||||
| 
 | ||||
|         if(localSearchCounter != searchCounter) return@launch | ||||
|         _searchResponse.postValue(data) | ||||
|         return@launch | ||||
|     } | ||||
| } | ||||
|  | @ -1,8 +1,8 @@ | |||
| <vector | ||||
|         xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|         android:name="vector" | ||||
|         android:width="850dp" | ||||
|         android:height="850dp" | ||||
|         android:width="24dp" | ||||
|         android:height="24dp" | ||||
|         android:viewportWidth="850" | ||||
|         android:viewportHeight="850"> | ||||
|     <path | ||||
|  |  | |||
|  | @ -73,7 +73,11 @@ | |||
|             android:id="@+id/cardSpace" | ||||
|             tools:listitem="@layout/search_result_grid" | ||||
|             android:orientation="vertical" | ||||
|     > | ||||
|     </com.lagradost.cloudstream3.ui.AutofitRecyclerView> | ||||
| 
 | ||||
|     /> | ||||
|     <androidx.recyclerview.widget.RecyclerView | ||||
|             android:id="@+id/search_master_recycler" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|             tools:listitem="@layout/homepage_parent" | ||||
|     /> | ||||
| </LinearLayout> | ||||
|  | @ -23,9 +23,9 @@ | |||
| 
 | ||||
|     <color name="white">#FFF</color> | ||||
|     <color name="dubColor">#3d50fa</color> <!--3b65f5 f18c82 8294F1--> | ||||
|     <color name="dubColorBg">#4D3B65F5</color> | ||||
|     <color name="dubColorBg">#803B65F5</color> | ||||
|     <color name="subColor">#F54A3B</color> <!--F53B66 FA3D79--> | ||||
|     <color name="subColorBg">#4DF53B66</color> | ||||
|     <color name="subColorBg">#80F53B66</color> | ||||
|     <color name="typeColor">#F54A3B</color> | ||||
|     <color name="typeColorBg">#4DF54A3B</color> | ||||
| 
 | ||||
|  |  | |||
|  | @ -58,6 +58,19 @@ | |||
|         /> | ||||
| 
 | ||||
|     </PreferenceCategory> | ||||
|     <PreferenceCategory | ||||
|             android:key="search" | ||||
|             android:title="Search" | ||||
|             app:isPreferenceVisible="true" | ||||
|     > | ||||
|         <SwitchPreference | ||||
|                 android:icon="@drawable/search_icon" | ||||
|                 app:key="advanced_search" | ||||
|                 android:title="Advanced Search" | ||||
|                 android:summary="Gives you the search results separated by provider" | ||||
|                 app:defaultValue="true" | ||||
|         /> | ||||
|     </PreferenceCategory> | ||||
| 
 | ||||
|     <PreferenceCategory | ||||
|             android:key="info" | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue