From 1a0b18dd55d6fdf6646719de96ef77e33aa524ca Mon Sep 17 00:00:00 2001 From: daigorian Date: Sun, 26 Sep 2021 04:00:49 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=A1=E3=82=A4=E3=83=B3=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E3=81=A8=E8=A9=B3=E7=B4=B0=E7=94=BB=E9=9D=A2=E3=81=A7=E7=94=BB?= =?UTF-8?q?=E9=9D=A2=E5=88=87=E3=82=8A=E6=9B=BF=E3=81=88=E3=81=8B=E3=82=89?= =?UTF-8?q?=E6=88=BB=E3=81=A3=E3=81=A6=E3=81=8D=E3=81=9F=E3=81=A8=E3=81=8D?= =?UTF-8?q?=EF=BC=88onResume=E3=81=97=E3=81=9F=E3=81=A8=E3=81=8D=EF=BC=89?= =?UTF-8?q?=E3=83=AA=E3=82=B9=E3=83=88=E3=81=AE=E4=B8=AD=E8=BA=AB=E3=82=92?= =?UTF-8?q?=E3=82=A2=E3=83=83=E3=83=97=E3=83=87=E3=83=BC=E3=83=88=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DeleteEnabledArrayObjectAdapter.kt | 177 ++++++++- .../com/daigorian/epcltvapp/MainFragment.kt | 350 ++++++++++++------ .../epcltvapp/VideoDetailsFragment.kt | 244 +++++++----- .../epgstationv2caller/GetRecordedParamV2.kt | 2 +- 4 files changed, 543 insertions(+), 230 deletions(-) diff --git a/app/src/main/java/com/daigorian/epcltvapp/DeleteEnabledArrayObjectAdapter.kt b/app/src/main/java/com/daigorian/epcltvapp/DeleteEnabledArrayObjectAdapter.kt index 175ed7a..3edc912 100644 --- a/app/src/main/java/com/daigorian/epcltvapp/DeleteEnabledArrayObjectAdapter.kt +++ b/app/src/main/java/com/daigorian/epcltvapp/DeleteEnabledArrayObjectAdapter.kt @@ -1,9 +1,18 @@ package com.daigorian.epcltvapp -import androidx.leanback.widget.ArrayObjectAdapter -import androidx.leanback.widget.ListRow -import androidx.leanback.widget.Presenter -import androidx.leanback.widget.PresenterSelector +import android.content.Context +import android.util.Log +import android.widget.Toast +import androidx.leanback.widget.* +import com.daigorian.epcltvapp.epgstationcaller.EpgStation +import com.daigorian.epcltvapp.epgstationcaller.GetRecordedParam +import com.daigorian.epcltvapp.epgstationcaller.GetRecordedResponse +import com.daigorian.epcltvapp.epgstationv2caller.EpgStationV2 +import com.daigorian.epcltvapp.epgstationv2caller.GetRecordedParamV2 +import com.daigorian.epcltvapp.epgstationv2caller.Records +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response import kotlin.reflect.typeOf open class DeleteEnabledArrayObjectAdapter : ArrayObjectAdapter { @@ -31,5 +40,165 @@ open class DeleteEnabledArrayObjectAdapter : ArrayObjectAdapter { } } + fun getListRowByHeaderId(headerId : Long):ListRow?{ + var verticalIndex = 0 + while(verticalIndex < size()) { + val row = get(verticalIndex) + if(row is ListRow){ + if(row.headerItem.id == headerId){ + return row + } + } + verticalIndex += 1 + } + return null + } + + fun updateContentsListRow(v1Pram: GetRecordedParam, v2Param: GetRecordedParamV2, title:String, id:Long,presenter:Presenter,context: Context){ + + + // 同じIDを持つ行が存在するかどうか確認する + val listRow = getListRowByHeaderId(id) + + // 既存の行があれば、それを取得する。なければ新たに作る。 + val listRowAdapter = if(listRow==null) + ArrayObjectAdapter(presenter) + else + listRow.adapter as ArrayObjectAdapter + + // 既存の行がなければ、新たに作った行を追加する。 + if(listRow==null){ + val header = HeaderItem( id ,title) + add(ListRow(header, listRowAdapter)) + } + + // すでにロードされている数。 + val numOfLoaded = if (listRow==null) + 0L + else + listRowAdapter.size().toLong() + + // APIのコールバックでListRowの中身をセットするように仕掛ける + // EPGStation V1.x.x + EpgStation.api?.getRecorded( + limit = if(numOfLoaded>v1Pram.limit) numOfLoaded else v1Pram.limit, + offset = v1Pram.offset, + reverse = v1Pram.reverse, + rule = v1Pram.rule, + genre1 = v1Pram.genre1, + channel = v1Pram.channel, + keyword = v1Pram.keyword, + hasTs = v1Pram.hasTs, + recording = v1Pram.recording )?.enqueue(object : Callback { + + override fun onResponse(call: Call, response: Response) { + response.body()?.let { getRecordedResponse -> + + //既存のリストにあって、レスポンスにないアイテムの削除 + var horizontalIndex = 0 + while(horizontalIndex < listRowAdapter.size()) { + var found = false + getRecordedResponse.recorded.forEach { + if(listRowAdapter.get(horizontalIndex).equals(it) ) found = true + } + if (!found) { + listRowAdapter.removeItems(horizontalIndex,1) + }else { + horizontalIndex += 1 + } + } + + //レスポンスにあって、既存のリストにないアイテムの追加 + getRecordedResponse.recorded.forEachIndexed { index, it -> + if(listRowAdapter.indexOf(it) == -1){ + listRowAdapter.add(index,it) + } + } + + //続きがあるなら"次を読み込む"を置く。 + val numOfItem = getRecordedResponse.recorded.count().toLong() + if (numOfItem < getRecordedResponse.total) { + listRowAdapter.add( + GetRecordedParam(limit = v1Pram.limit, + offset = numOfItem, + reverse = v1Pram.reverse, + rule = v1Pram.rule, + genre1 = v1Pram.genre1, + channel = v1Pram.channel, + keyword = v1Pram.keyword, + hasTs = v1Pram.hasTs, + recording = v1Pram.recording) + ) + } + + } + } + override fun onFailure(call: Call, t: Throwable) { + Log.d(TAG,"updateContentsListRow() getRecorded API Failure") + Toast.makeText(context, context.getText(R.string.connect_epgstation_failed), Toast.LENGTH_LONG).show() + } + }) + EpgStationV2.api?.getRecorded( + isHalfWidth = v2Param.isHalfWidth, + offset = v2Param.offset, + limit = if(numOfLoaded>v2Param.limit) numOfLoaded else v2Param.limit , + isReverse = v2Param.isReverse, + ruleId = v2Param.ruleId, + channelId = v2Param.channelId, + genre = v2Param.genre, + keyword = v2Param.keyword, + hasOriginalFile = v2Param.hasOriginalFile )?.enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + response.body()?.let { getRecordedResponse -> + + //既存のリストにあって、レスポンスにないアイテムの削除 + var horizontalIndex = 0 + while(horizontalIndex < listRowAdapter.size()) { + var found = false + getRecordedResponse.records.forEach { + if(listRowAdapter.get(horizontalIndex).equals(it) ) found = true + } + if (!found) { + listRowAdapter.removeItems(horizontalIndex,1) + }else { + horizontalIndex += 1 + } + } + + //レスポンスにあって、既存のリストにないアイテムの追加 + getRecordedResponse.records.forEachIndexed { index, it -> + if(listRowAdapter.indexOf(it) == -1){ + listRowAdapter.add(index,it) + } + } + //続きがあるなら"次を読み込む"を置く。 + val numOfItem = getRecordedResponse.records.count().toLong() + if (numOfItem < getRecordedResponse.total) { + listRowAdapter.add( + GetRecordedParamV2( + isHalfWidth = v2Param.isHalfWidth, + offset = numOfItem, + limit = v2Param.limit, + isReverse = v2Param.isReverse, + ruleId = v2Param.ruleId, + channelId = v2Param.channelId, + genre = v2Param.genre, + keyword = v2Param.keyword, + hasOriginalFile = v2Param.hasOriginalFile) + ) + } + } + } + override fun onFailure(call: Call, t: Throwable) { + Log.d(TAG,"updateContentsListRow() getRecorded API Failure") + Toast.makeText(context, context.getText(R.string.connect_epgstation_failed), Toast.LENGTH_LONG).show() + } + }) + + } + companion object{ + private const val TAG = "D.E.ArrayObjectAdapter" + } + } \ No newline at end of file diff --git a/app/src/main/java/com/daigorian/epcltvapp/MainFragment.kt b/app/src/main/java/com/daigorian/epcltvapp/MainFragment.kt index 1775665..3050989 100644 --- a/app/src/main/java/com/daigorian/epcltvapp/MainFragment.kt +++ b/app/src/main/java/com/daigorian/epcltvapp/MainFragment.kt @@ -85,23 +85,16 @@ class MainFragment : BrowseSupportFragment() { Log.i(TAG, "onResume") super.onResume() if(mNeedsReloadAllOnResume && SettingsFragment.isPreferenceAllExists(requireContext())) { - //設定画面から戻ってきたので設定を再読み込みする + //設定画面から戻ってきたのでEPGStationの接続からやり直す initEPGStationApi() mNeedsReloadAllOnResume = false - } - if(mNeedsReloadHistoryOnResume){ + }else if(mNeedsReloadHistoryOnResume) { //履歴行の読み直し mMainMenuAdapter.deleteCategory(Category.SEARCH_HISTORY) - SearchFragment.getHistory(requireContext()).asReversed().forEach{ - val historyRow = contentsListRowBuilder( - GetRecordedParam(keyword = it), - GetRecordedParamV2(keyword = it), - it - ) - mMainMenuAdapter.addToCategory(Category.SEARCH_HISTORY,historyRow) - } - mNeedsReloadHistoryOnResume = false - + updateRows() + }else{ + //コンテンツをアップデートする。 + updateRows() } } @@ -210,25 +203,61 @@ class MainFragment : BrowseSupportFragment() { searchAffordanceColor = ContextCompat.getColor(requireContext(), R.color.search_opaque) } - private fun loadRows() { + private fun updateRows() { - //内容クリア - mMainMenuAdapter.clear() + EpgStationV2.api?.let{ api -> + // EPGStation V2.x.x の場合だけ「録画中」列を作る + //行のID + val headerId = Category.ON_RECORDING.ordinal.toLong()*10000 + + //同じIDを持つ既存の行があるか検索 + val listRow = mMainMenuAdapter.getListRowByHeaderId(headerId) + + // 既存の行があれば、それを取得する。なければ新たに作る。 + val listRowAdapter = if(listRow==null) + ArrayObjectAdapter(mCardPresenter) + else + listRow.adapter as ArrayObjectAdapter - EpgStationV2.api?.let{ api -> - // EPGStation V2.x.x の場合だけ録画中列を作る - val listRowAdapter = ArrayObjectAdapter(mCardPresenter) - val header = HeaderItem( getString(R.string.now_on_recording)) - mMainMenuAdapter.addToCategory(Category.ON_RECORDING,ListRow(header, listRowAdapter)) - api.getRecording().enqueue(object : Callback { + // 既存の行がなければ、新たに作った行を追加する。 + if(listRow==null){ + val header = HeaderItem( headerId , getString(R.string.now_on_recording)) + mMainMenuAdapter.addToCategory(Category.ON_RECORDING,ListRow(header, listRowAdapter)) + } + + // APIでロードするアイテムの数。既存のアイテムがある場合はその数だけロードする + val apiLimit = if (listRow==null) + EpgStationV2.default_limit.toInt() + else + listRowAdapter.size() + + + api.getRecording(limit = apiLimit).enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { response.body()?.let { getRecordingResponse -> if (getRecordingResponse.records.isNotEmpty()) { - //"録画中"の列を追加。 - getRecordingResponse.records.forEach { - listRowAdapter.add(it) + + //既存のリストにあって、レスポンスにないアイテムの削除 + var horizontalIndex = 0 + while(horizontalIndex < listRowAdapter.size()) { + var found = false + getRecordingResponse.records.forEach { + if(listRowAdapter.get(horizontalIndex).equals(it) ) found = true + } + if (!found) { + listRowAdapter.removeItems(horizontalIndex,1) + }else { + horizontalIndex += 1 + } + } + + //レスポンスにあって、既存のリストにないアイテムの追加 + getRecordingResponse.records.forEachIndexed { index, it -> + if(listRowAdapter.indexOf(it) == -1){ + listRowAdapter.add(index,it) + } } } } @@ -241,22 +270,24 @@ class MainFragment : BrowseSupportFragment() { } //最近の録画の列 - val recentlyRecorded = contentsListRowBuilder( + mMainMenuAdapter.updateContentsListRowWithCategory( GetRecordedParam(), GetRecordedParamV2(), - getString(R.string.recent_videos) + getString(R.string.recent_videos), + Category.RECENTLY_RECORDED, + 0L ) - mMainMenuAdapter.addToCategory(Category.RECENTLY_RECORDED,recentlyRecorded) //履歴行の追加 - SearchFragment.getHistory(requireContext()).asReversed().forEach{ - val historyRow = contentsListRowBuilder( + SearchFragment.getHistory(requireContext()).asReversed().forEachIndexed{ index, it -> + mMainMenuAdapter.updateContentsListRowWithCategory( GetRecordedParam(keyword = it), GetRecordedParamV2(keyword = it), - it + it, + Category.SEARCH_HISTORY, + index.toLong() ) - mMainMenuAdapter.addToCategory(Category.SEARCH_HISTORY,historyRow) } //ルールの並び順を表すフラグ。デフォルトfalse。 @@ -275,12 +306,13 @@ class MainFragment : BrowseSupportFragment() { }else{ rule.keyword } - val recordedByRule = contentsListRowBuilder( + mMainMenuAdapter.updateContentsListRowWithCategory( GetRecordedParam(rule= rule.id), GetRecordedParamV2(ruleId= rule.id), - keyword + keyword, + Category.RECORDED_BY_RULES, + rule.id ) - mMainMenuAdapter.addToCategory(Category.RECORDED_BY_RULES,recordedByRule) } } } @@ -301,12 +333,13 @@ class MainFragment : BrowseSupportFragment() { }else{ rule.searchOption?.keyword!! } - val recordedByRule = contentsListRowBuilder( + mMainMenuAdapter.updateContentsListRowWithCategory( GetRecordedParam(rule= rule.id), GetRecordedParamV2(ruleId= rule.id), - keyword + keyword, + Category.RECORDED_BY_RULES, + rule.id ) - mMainMenuAdapter.addToCategory(Category.RECORDED_BY_RULES,recordedByRule) } } } @@ -316,12 +349,25 @@ class MainFragment : BrowseSupportFragment() { } }) + } + + private fun loadRows() { + + //内容クリア + mMainMenuAdapter.clear() + + //コンテンツをロード。 + updateRows() + //"設定" のボタンが乗る行 val gridHeader = HeaderItem(getString(R.string.settings)) val gridPresenter = GridItemPresenter() val gridRowAdapter = ArrayObjectAdapter(gridPresenter) gridRowAdapter.add(resources.getString(R.string.settings)) gridRowAdapter.add(resources.getString(R.string.reload)) + + //ルールの並び順を表すフラグ。デフォルトfalse。 + val isNewestFirst = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(getString(R.string.pref_key_rules_order_is_newest_first),false) if (isNewestFirst){ gridRowAdapter.add(resources.getString(R.string.set_oldest_rule_first)) }else{ @@ -335,6 +381,7 @@ class MainFragment : BrowseSupportFragment() { } + private fun setupEventListeners() { setOnSearchClickedListener { Intent(activity, SearchActivity::class.java).also { intent -> @@ -419,91 +466,6 @@ class MainFragment : BrowseSupportFragment() { } } - private fun contentsListRowBuilder(v1Pram:GetRecordedParam,v2Param:GetRecordedParamV2,title:String):ListRow{ - // 空っぽの、タイトルだけ設定されたListRowを作る - val listRowAdapter = ArrayObjectAdapter(mCardPresenter) - val header = HeaderItem(title) - val result = ListRow(header, listRowAdapter) - - // APIのコールバックでListRowの中身をセットするように仕掛ける - // EPGStation V1.x.x - EpgStation.api?.getRecorded( - limit = v1Pram.limit, - offset = v1Pram.offset, - reverse = v1Pram.reverse, - rule = v1Pram.rule, - genre1 = v1Pram.genre1, - channel = v1Pram.channel, - keyword = v1Pram.keyword, - hasTs = v1Pram.hasTs, - recording = v1Pram.recording )?.enqueue(object : Callback { - - override fun onResponse(call: Call, response: Response) { - response.body()?.let { getRecordedResponse -> - //APIのレスポンスをひとつづつアイテムとして加える。 - getRecordedResponse.recorded.forEach { recordedProgram -> - listRowAdapter.add(recordedProgram) - } - //続きがあるなら"次を読み込む"を置く。 - val numOfItem = getRecordedResponse.recorded.count().toLong() - if (numOfItem < getRecordedResponse.total) { - listRowAdapter.add(GetRecordedParam(limit = v1Pram.limit, - offset = numOfItem, - reverse = v1Pram.reverse, - rule = v1Pram.rule, - genre1 = v1Pram.genre1, - channel = v1Pram.channel, - keyword = v1Pram.keyword, - hasTs = v1Pram.hasTs, - recording = v1Pram.recording)) - } - - } - } - override fun onFailure(call: Call, t: Throwable) { - Log.d(TAG,"loadRows() getRecorded API Failure") - Toast.makeText(context!!, getString(R.string.connect_epgstation_failed), Toast.LENGTH_LONG).show() - } - }) - EpgStationV2.api?.getRecorded( - isHalfWidth = v2Param.isHalfWidth, - offset = v2Param.offset, - limit = v2Param.limit, - isReverse = v2Param.isReverse, - ruleId = v2Param.ruleId, - channelId = v2Param.channelId, - genre = v2Param.genre, - keyword = v2Param.keyword, - hasOriginalFile = v2Param.hasOriginalFile )?.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - response.body()?.let { getRecordedResponse -> - //APIのレスポンスをひとつづつアイテムとして加える。 - getRecordedResponse.records.forEach { - listRowAdapter.add(it) - } - //続きがあるなら"次を読み込む"を置く。 - val numOfItem = getRecordedResponse.records.count().toLong() - if (numOfItem < getRecordedResponse.total) { - listRowAdapter.add(GetRecordedParamV2( - isHalfWidth = v2Param.isHalfWidth, - offset = numOfItem, - limit = v2Param.limit, - isReverse = v2Param.isReverse, - ruleId = v2Param.ruleId, - channelId = v2Param.channelId, - genre = v2Param.genre, - keyword = v2Param.keyword, - hasOriginalFile = v2Param.hasOriginalFile)) - } - } - } - override fun onFailure(call: Call, t: Throwable) { - Log.d(TAG,"loadRows() getRecorded API Failure") - Toast.makeText(context!!, getString(R.string.connect_epgstation_failed), Toast.LENGTH_LONG).show() - } - }) - return result - } private inner class ItemViewSelectedListener : OnItemViewSelectedListener { @@ -747,6 +709,148 @@ class MainFragment : BrowseSupportFragment() { }//synchronized } + fun updateContentsListRowWithCategory(v1Pram:GetRecordedParam,v2Param:GetRecordedParamV2,title:String,category:Category,idInCategory:Long){ + + val headerId = category.ordinal.toLong()*10000 + idInCategory + + // 同じIDを持つ行が存在するかどうか確認する + val listRow = getListRowByHeaderId(headerId) + + // 既存の行があれば、それを取得する。なければ新たに作る。 + val listRowAdapter = if(listRow==null) + ArrayObjectAdapter(mCardPresenter) + else + listRow.adapter as ArrayObjectAdapter + + // 既存の行がなければ、新たに作った行を追加する。 + if(listRow==null){ + val header = HeaderItem( headerId ,title) + addToCategory(category,ListRow(header, listRowAdapter)) + } + + // すでにロードされている数。 + val numOfLoaded = if (listRow==null) + 0L + else + listRowAdapter.size().toLong() + + // APIのコールバックでListRowの中身をセットするように仕掛ける + // EPGStation V1.x.x + EpgStation.api?.getRecorded( + limit = if(numOfLoaded>v1Pram.limit) numOfLoaded else v1Pram.limit, + offset = v1Pram.offset, + reverse = v1Pram.reverse, + rule = v1Pram.rule, + genre1 = v1Pram.genre1, + channel = v1Pram.channel, + keyword = v1Pram.keyword, + hasTs = v1Pram.hasTs, + recording = v1Pram.recording )?.enqueue(object : Callback { + + override fun onResponse(call: Call, response: Response) { + response.body()?.let { getRecordedResponse -> + + //既存のリストにあって、レスポンスにないアイテムの削除 + var horizontalIndex = 0 + while(horizontalIndex < listRowAdapter.size()) { + var found = false + getRecordedResponse.recorded.forEach { + if(listRowAdapter.get(horizontalIndex).equals(it) ) found = true + } + if (!found) { + listRowAdapter.removeItems(horizontalIndex,1) + }else { + horizontalIndex += 1 + } + } + + //レスポンスにあって、既存のリストにないアイテムの追加 + getRecordedResponse.recorded.forEachIndexed { index, it -> + if(listRowAdapter.indexOf(it) == -1){ + listRowAdapter.add(index,it) + } + } + + //続きがあるなら"次を読み込む"を置く。 + val numOfItem = getRecordedResponse.recorded.count().toLong() + if (numOfItem < getRecordedResponse.total) { + listRowAdapter.add(GetRecordedParam(limit = v1Pram.limit, + offset = numOfItem, + reverse = v1Pram.reverse, + rule = v1Pram.rule, + genre1 = v1Pram.genre1, + channel = v1Pram.channel, + keyword = v1Pram.keyword, + hasTs = v1Pram.hasTs, + recording = v1Pram.recording)) + } + + } + } + override fun onFailure(call: Call, t: Throwable) { + Log.d(TAG,"loadRows() getRecorded API Failure") + Toast.makeText(context!!, getString(R.string.connect_epgstation_failed), Toast.LENGTH_LONG).show() + } + }) + EpgStationV2.api?.getRecorded( + isHalfWidth = v2Param.isHalfWidth, + offset = v2Param.offset, + limit = if(numOfLoaded>v2Param.limit) numOfLoaded else v2Param.limit , + isReverse = v2Param.isReverse, + ruleId = v2Param.ruleId, + channelId = v2Param.channelId, + genre = v2Param.genre, + keyword = v2Param.keyword, + hasOriginalFile = v2Param.hasOriginalFile )?.enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + response.body()?.let { getRecordedResponse -> + + //既存のリストにあって、レスポンスにないアイテムの削除 + var horizontalIndex = 0 + while(horizontalIndex < listRowAdapter.size()) { + var found = false + getRecordedResponse.records.forEach { + if(listRowAdapter.get(horizontalIndex).equals(it) ) found = true + } + if (!found) { + listRowAdapter.removeItems(horizontalIndex,1) + }else { + horizontalIndex += 1 + } + } + + //レスポンスにあって、既存のリストにないアイテムの追加 + getRecordedResponse.records.forEachIndexed { index, it -> + if(listRowAdapter.indexOf(it) == -1){ + listRowAdapter.add(index,it) + } + } + //続きがあるなら"次を読み込む"を置く。 + val numOfItem = getRecordedResponse.records.count().toLong() + if (numOfItem < getRecordedResponse.total) { + listRowAdapter.add(GetRecordedParamV2( + isHalfWidth = v2Param.isHalfWidth, + offset = numOfItem, + limit = v2Param.limit, + isReverse = v2Param.isReverse, + ruleId = v2Param.ruleId, + channelId = v2Param.channelId, + genre = v2Param.genre, + keyword = v2Param.keyword, + hasOriginalFile = v2Param.hasOriginalFile)) + } + } + } + override fun onFailure(call: Call, t: Throwable) { + Log.d(TAG,"loadRows() getRecorded API Failure") + Toast.makeText(context!!, getString(R.string.connect_epgstation_failed), Toast.LENGTH_LONG).show() + } + }) + + } + + + fun sortRulesByRecordedDate(){ synchronized(this){ diff --git a/app/src/main/java/com/daigorian/epcltvapp/VideoDetailsFragment.kt b/app/src/main/java/com/daigorian/epcltvapp/VideoDetailsFragment.kt index 32ad118..68999bd 100644 --- a/app/src/main/java/com/daigorian/epcltvapp/VideoDetailsFragment.kt +++ b/app/src/main/java/com/daigorian/epcltvapp/VideoDetailsFragment.kt @@ -20,9 +20,11 @@ import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import com.daigorian.epcltvapp.epgstationcaller.EpgStation +import com.daigorian.epcltvapp.epgstationcaller.GetRecordedParam import com.daigorian.epcltvapp.epgstationcaller.GetRecordedResponse import com.daigorian.epcltvapp.epgstationcaller.RecordedProgram import com.daigorian.epcltvapp.epgstationv2caller.EpgStationV2 +import com.daigorian.epcltvapp.epgstationv2caller.GetRecordedParamV2 import com.daigorian.epcltvapp.epgstationv2caller.RecordedItem import com.daigorian.epcltvapp.epgstationv2caller.Records import retrofit2.Call @@ -64,10 +66,11 @@ class VideoDetailsFragment : DetailsSupportFragment() { mCardPresenter.objAdapter = mAdapter setupDetailsOverviewRow() setupDetailsOverviewRowPresenter() - setupRelatedMovieListRow() + updateRelatedMovieListRow() adapter = mAdapter initializeBackground(EpgStation.getThumbnailURL(mSelectedRecordedProgram?.id.toString())) onItemViewClickedListener = ItemViewClickedListener() + setOnItemViewSelectedListener( ItemViewSelectedListener()) } mSelectedRecordedItem != null -> { // EPGStation Version 2.x.x @@ -76,7 +79,7 @@ class VideoDetailsFragment : DetailsSupportFragment() { mCardPresenter.objAdapter = mAdapter setupDetailsOverviewRow() setupDetailsOverviewRowPresenter() - setupRelatedMovieListRow() + updateRelatedMovieListRow() adapter = mAdapter initializeBackground( EpgStationV2.getThumbnailURL( @@ -88,6 +91,7 @@ class VideoDetailsFragment : DetailsSupportFragment() { }) ) onItemViewClickedListener = ItemViewClickedListener() + setOnItemViewSelectedListener( ItemViewSelectedListener()) } else -> { val intent = Intent(requireContext(), MainActivity::class.java) @@ -96,6 +100,11 @@ class VideoDetailsFragment : DetailsSupportFragment() { } } + override fun onResume() { + super.onResume() + updateRelatedMovieListRow() + } + private fun initializeBackground(imageURL: String) { mDetailsBackground.enableParallax() @@ -249,6 +258,8 @@ class VideoDetailsFragment : DetailsSupportFragment() { } private fun setupDetailsOverviewRowPresenter() { + Log.d(TAG, "setupDetailsOverviewRowPresenter()") + // Set detail background. val detailsPresenter = FullWidthDetailsOverviewRowPresenter(DetailsDescriptionPresenter()) detailsPresenter.backgroundColor = @@ -322,119 +333,51 @@ class VideoDetailsFragment : DetailsSupportFragment() { } - private fun setupRelatedMovieListRow() { + private fun updateRelatedMovieListRow() { + Log.d(TAG, "updateRelatedMovieListRow()") // 関連動画一覧の生成 // - 現在表示中の動画の名前にregexDelimiterがあれば、その前までの文字列をシリーズ名とみなして一覧を表示 // - 現在表示中の動画と同じルールIDを持った動画を検索して一覧を表示 - val regexDelimiter = """(?!^)(([\s ]?([##♯第][0-9]{1,3}|[0-9]{1,3}[話回]|\([0-9]{1,3}\)|[「【『<])|\[[新字デ解再無映終多]\]|\(吹\))|([\s ][^0-9\s ]+[\s ]?[0-9]{2,3}))""".toRegex() + val regexDelimiter = """(?!^)(([\s ]?([##♯第][0-90-9]{1,3}|[0-90-9]{1,3}[話回]|\([0-90-9]{1,3}\)|[「【『<])|\[[新字デ解再無映終多]\]|\(吹\))|([\s ][^0-90-9\s ]+[\s ]?[0-90-9]{2,3}))""".toRegex() val regexDeleteStr = """^(\[[新字デ解再無映終多]\])|\(吹\)""".toRegex() - // EPGStation Version 1.x.x - mSelectedRecordedProgram?.let{ recorded_program -> - - // 行頭に[新]などがあった場合は消しておく - val programNameStriped = recorded_program.name.replace(regexDeleteStr,"") - // 名前にregexDelimiterがあった場合はそこで区切る - val programName = programNameStriped.split(regexDelimiter) - if (programName.size > 1 ) { - - val listRowAdapter = ArrayObjectAdapter(mCardPresenter) - val searchKeyword = programName[0] - EpgStation.api?.getRecorded(keyword = searchKeyword)?.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - response.body()?.recorded?.forEach { - listRowAdapter.add(it) - } - } - override fun onFailure(call: Call, t: Throwable) { - Log.d(TAG,"setupRelatedMovieListRow() getRecorded API Failure") - Toast.makeText(context!!, getString(R.string.connect_epgstation_failed), Toast.LENGTH_SHORT).show() - } - }) - - val header = HeaderItem(searchKeyword) - mAdapter.add(ListRow(header, listRowAdapter)) - mPresenterSelector.addClassPresenter(ListRow::class.java, ListRowPresenter()) - } - // ルールIDがある場合は、同じルールIDの動画のリストを作成する - recorded_program.ruleId?.let{ rule_id -> + // 番組名 originalTitle + val originalTitle :String =if(mSelectedRecordedProgram!=null) mSelectedRecordedProgram!!.name + else mSelectedRecordedItem!!.name - val listRowAdapter = ArrayObjectAdapter(mCardPresenter) - EpgStation.api?.getRecorded(rule = rule_id)?.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - response.body()?.recorded?.forEach { - listRowAdapter.add(it) - } - } - override fun onFailure(call: Call, t: Throwable) { - Log.d(TAG,"setupRelatedMovieListRow() getRecorded API Failure") - Toast.makeText(context!!, getString(R.string.connect_epgstation_failed), Toast.LENGTH_SHORT).show() - } - }) + // 行頭に[新]などがあった場合は消しておく + val programNameStriped = originalTitle.replace(regexDeleteStr,"") - val header = HeaderItem(getString(R.string.videos_in_same_rule)) - mAdapter.add(ListRow(header, listRowAdapter)) - mPresenterSelector.addClassPresenter(ListRow::class.java, ListRowPresenter()) - } + // 名前にregexDelimiterがあった場合はそこで区切る + val programName = programNameStriped.split(regexDelimiter) + // 区切り文字が見つかった場合、シリーズとして表示する + if (programName.size > 1 ) { + val searchKeyword = programName[0] + mAdapter.updateContentsListRow( + GetRecordedParam(keyword = searchKeyword), + GetRecordedParamV2(keyword = searchKeyword), + searchKeyword, + 0, + mCardPresenter, + requireContext() + ) } - // EPGStation Version 2.x.x - mSelectedRecordedItem?.let{ recorded_item -> - - // 行頭に[新]などがあった場合は消しておく - val programNameStriped =recorded_item.name.replace(regexDeleteStr,"") - // 名前にregexDelimiterがあった場合はそこで区切る - val programName = programNameStriped.split(regexDelimiter) - if (programName.size > 1 ) { - - val listRowAdapter = ArrayObjectAdapter(mCardPresenter) - - val searchKeyword = programName[0] - EpgStationV2.api?.getRecorded(keyword = searchKeyword)?.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - response.body()?.records?.forEach { - listRowAdapter.add(it) - } - } - override fun onFailure(call: Call, t: Throwable) { - Log.d(TAG,"setupRelatedMovieListRow() getRecorded API Failure") - Toast.makeText(context!!, getString(R.string.connect_epgstation_failed), Toast.LENGTH_SHORT).show() - } - }) - - val header = HeaderItem(searchKeyword) - mAdapter.add(ListRow(header, listRowAdapter)) - mPresenterSelector.addClassPresenter(ListRow::class.java, ListRowPresenter()) - } - // ルールIDがある場合は、同じルールIDの動画のリストを作成する - recorded_item.ruleId?.let { rule_id -> - val listRowAdapter = ArrayObjectAdapter(mCardPresenter) - EpgStationV2.api?.getRecorded(ruleId = rule_id) - ?.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - response.body()?.records?.forEach { - listRowAdapter.add(it) - } - } + val ruleId =if(mSelectedRecordedProgram!=null) mSelectedRecordedProgram!!.ruleId + else mSelectedRecordedItem!!.ruleId - override fun onFailure(call: Call, t: Throwable) { - Log.d(TAG, "setupRelatedMovieListRow() getRecorded API Failure") - Toast.makeText( - context!!, - getString(R.string.connect_epgstation_failed), - Toast.LENGTH_SHORT - ).show() - } - }) - - val header = HeaderItem(getString(R.string.videos_in_same_rule)) - mAdapter.add(ListRow(header, listRowAdapter)) - mPresenterSelector.addClassPresenter(ListRow::class.java, ListRowPresenter()) - } - } + mAdapter.updateContentsListRow( + GetRecordedParam(rule = ruleId), + GetRecordedParamV2(ruleId = ruleId), + "", + 1, + mCardPresenter, + requireContext() + ) + mPresenterSelector.addClassPresenter(ListRow::class.java, ListRowPresenter()) } @@ -482,6 +425,103 @@ class VideoDetailsFragment : DetailsSupportFragment() { } } + private inner class ItemViewSelectedListener : OnItemViewSelectedListener { + override fun onItemSelected( + itemViewHolder: Presenter.ViewHolder?, item: Any?, + rowViewHolder: RowPresenter.ViewHolder, row: Row + ) { + when (item) { + is GetRecordedParam -> { + // EPGStation Version 1.x.x の続きを取得するアイテム + val adapter = ((row as ListRow).adapter as ArrayObjectAdapter) + + //APIで続きを取得して続きに加えていく + // EPGStation V1.x.x + EpgStation.api?.getRecorded( + limit = item.limit, + offset = item.offset, + reverse = item.reverse, + rule = item.rule, + genre1 = item.genre1, + channel = item.channel, + keyword = item.keyword, + hasTs = item.hasTs, + recording = item.recording + )?.enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + response.body()?.let { getRecordedResponse -> + + //APIのレスポンスをひとつづつアイテムとして加える。最初のアイテムだけ、Loadingアイテムを置き換える + //先にremoveしてaddすると高速でスクロールさせたときに描画とremoveがぶつかって落ちるのであえてreplaceに。 + getRecordedResponse.recorded.forEachIndexed { index, recordedProgram -> + if(index == 0) { + adapter.replace(adapter.indexOf(item),recordedProgram) + }else{ + adapter.add(recordedProgram) + } + } + //続きがあるなら"次を読み込む"を置く。 + val numOfItem = getRecordedResponse.recorded.count().toLong() + item.offset + if (numOfItem < getRecordedResponse.total) { + adapter.add(item.copy(offset = numOfItem)) + } + + } + } + override fun onFailure(call: Call, t: Throwable) { + Log.d(TAG,"onItemSelected() getRecorded API Failure") + Toast.makeText(context!!, getString(R.string.connect_epgstation_failed), Toast.LENGTH_LONG).show() + } + }) + } + is GetRecordedParamV2 -> { + // EPGStation Version 1.x.x の続きを取得するアイテム + val adapter = ((row as ListRow).adapter as ArrayObjectAdapter) + + //APIで続きを取得して続きに加えていく + // EPGStation V2.x.x + EpgStationV2.api?.getRecorded( + isHalfWidth = item.isHalfWidth, + offset = item.offset, + limit = item.limit, + isReverse = item.isReverse, + ruleId = item.ruleId, + channelId = item.channelId, + genre = item.genre, + keyword = item.keyword, + hasOriginalFile = item.hasOriginalFile + )?.enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + response.body()?.let { responseRoot -> + + //APIのレスポンスをひとつづつアイテムとして加える。最初のアイテムだけ、Loadingアイテムを置き換える + //先にremoveしてaddすると高速でスクロールさせたときに描画とremoveがぶつかって落ちるのであえてreplaceに。 + responseRoot.records.forEachIndexed { index, recordedProgram -> + if(index == 0) { + adapter.replace(adapter.indexOf(item),recordedProgram) + }else{ + adapter.add(recordedProgram) + } + } + //続きがあるなら"次を読み込む"を置く。 + val numOfItem = responseRoot.records.count().toLong() + item.offset + if (numOfItem < responseRoot.total) { + adapter.add(item.copy(offset = numOfItem)) + } + + } + } + override fun onFailure(call: Call, t: Throwable) { + Log.d(TAG,"onItemSelected() getRecorded API Failure") + Toast.makeText(context!!, getString(R.string.connect_epgstation_failed), Toast.LENGTH_LONG).show() + } + }) + } + + } + } + } + companion object { private const val TAG = "VideoDetailsFragment" diff --git a/app/src/main/java/com/daigorian/epcltvapp/epgstationv2caller/GetRecordedParamV2.kt b/app/src/main/java/com/daigorian/epcltvapp/epgstationv2caller/GetRecordedParamV2.kt index 637a097..dee10cf 100644 --- a/app/src/main/java/com/daigorian/epcltvapp/epgstationv2caller/GetRecordedParamV2.kt +++ b/app/src/main/java/com/daigorian/epcltvapp/epgstationv2caller/GetRecordedParamV2.kt @@ -3,7 +3,7 @@ package com.daigorian.epcltvapp.epgstationv2caller import retrofit2.http.Query data class GetRecordedParamV2( - val isHalfWidth: Boolean = false, + val isHalfWidth: Boolean = true, val offset: Long = 0, val limit: Long = EpgStationV2.default_limit.toLong(), val isReverse: Boolean = false,