mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-04-07 21:21:23 +00:00
Merge pull request #14503 from Simonx22/android/convert-syncprogramsjobservice-to-kotlin
Android: Convert SyncProgramsJobService to Kotlin
This commit is contained in:
commit
3bc6815c8b
@ -1,164 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.services;
|
||||
|
||||
import android.app.job.JobParameters;
|
||||
import android.app.job.JobService;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.PersistableBundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.tvprovider.media.tv.Channel;
|
||||
import androidx.tvprovider.media.tv.PreviewProgram;
|
||||
import androidx.tvprovider.media.tv.TvContractCompat;
|
||||
|
||||
import org.dolphinemu.dolphinemu.R;
|
||||
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||
import org.dolphinemu.dolphinemu.ui.platform.PlatformTab;
|
||||
import org.dolphinemu.dolphinemu.utils.AppLinkHelper;
|
||||
import org.dolphinemu.dolphinemu.utils.TvUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class SyncProgramsJobService extends JobService
|
||||
{
|
||||
private static final String TAG = "SyncProgramsJobService";
|
||||
|
||||
private SyncProgramsTask mSyncProgramsTask;
|
||||
|
||||
@Override
|
||||
public boolean onStartJob(final JobParameters jobParameters)
|
||||
{
|
||||
Log.d(TAG, "onStartJob(): " + jobParameters);
|
||||
final long channelId = getChannelId(jobParameters);
|
||||
if (channelId == -1L)
|
||||
{
|
||||
Log.d(TAG, "Failed to find channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
mSyncProgramsTask =
|
||||
new SyncProgramsTask(getApplicationContext())
|
||||
{
|
||||
@Override
|
||||
protected void onPostExecute(Boolean finished)
|
||||
{
|
||||
super.onPostExecute(finished);
|
||||
mSyncProgramsTask = null;
|
||||
jobFinished(jobParameters, !finished);
|
||||
}
|
||||
};
|
||||
mSyncProgramsTask.execute(channelId);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStopJob(JobParameters jobParameters)
|
||||
{
|
||||
if (mSyncProgramsTask != null)
|
||||
{
|
||||
mSyncProgramsTask.cancel(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private long getChannelId(JobParameters jobParameters)
|
||||
{
|
||||
PersistableBundle extras = jobParameters.getExtras();
|
||||
return extras.getLong(TvContractCompat.EXTRA_CHANNEL_ID, -1L);
|
||||
}
|
||||
|
||||
private static class SyncProgramsTask extends AsyncTask<Long, Void, Boolean>
|
||||
{
|
||||
private Context context;
|
||||
private List<GameFile> updatePrograms;
|
||||
|
||||
private SyncProgramsTask(Context context)
|
||||
{
|
||||
this.context = context;
|
||||
updatePrograms = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines which channel to update, get the game files for the channel,
|
||||
* then updates the list
|
||||
*/
|
||||
@Override
|
||||
protected Boolean doInBackground(Long... channelIds)
|
||||
{
|
||||
List<Long> params = Arrays.asList(channelIds);
|
||||
if (!params.isEmpty())
|
||||
{
|
||||
for (Long channelId : params)
|
||||
{
|
||||
Channel channel = TvUtil.getChannelById(context, channelId);
|
||||
for (PlatformTab platformTab : PlatformTab.values())
|
||||
{
|
||||
if (channel != null &&
|
||||
channel.getAppLinkIntentUri().equals(AppLinkHelper.buildBrowseUri(platformTab)))
|
||||
{
|
||||
getGamesByPlatform(platformTab);
|
||||
syncPrograms(channelId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void getGamesByPlatform(PlatformTab platformTab)
|
||||
{
|
||||
updatePrograms = GameFileCacheManager.getGameFilesForPlatformTab(platformTab);
|
||||
}
|
||||
|
||||
private void syncPrograms(long channelId)
|
||||
{
|
||||
Log.d(TAG, "Sync programs for channel: " + channelId);
|
||||
deletePrograms(channelId);
|
||||
createPrograms(channelId);
|
||||
}
|
||||
|
||||
private void createPrograms(long channelId)
|
||||
{
|
||||
for (GameFile game : updatePrograms)
|
||||
{
|
||||
PreviewProgram previewProgram = buildProgram(channelId, game);
|
||||
|
||||
context.getContentResolver()
|
||||
.insert(
|
||||
TvContractCompat.PreviewPrograms.CONTENT_URI,
|
||||
previewProgram.toContentValues());
|
||||
}
|
||||
}
|
||||
|
||||
private void deletePrograms(long channelId)
|
||||
{
|
||||
context.getContentResolver().delete(
|
||||
TvContractCompat.buildPreviewProgramsUriForChannel(channelId),
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
private PreviewProgram buildProgram(long channelId, GameFile game)
|
||||
{
|
||||
Uri appLinkUri = AppLinkHelper.buildGameUri(channelId, game.getGameId());
|
||||
Uri banner = TvUtil.buildBanner(game, context);
|
||||
if (banner == null)
|
||||
banner = TvUtil.getUriToResource(context, R.drawable.banner_tv);
|
||||
|
||||
PreviewProgram.Builder builder = new PreviewProgram.Builder();
|
||||
builder.setChannelId(channelId)
|
||||
.setType(TvContractCompat.PreviewProgramColumns.TYPE_GAME)
|
||||
.setTitle(game.getTitle())
|
||||
.setDescription(game.getDescription())
|
||||
.setPosterArtUri(banner)
|
||||
.setPosterArtAspectRatio(TvContractCompat.PreviewPrograms.ASPECT_RATIO_2_3)
|
||||
.setIntentUri(appLinkUri);
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,156 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.services
|
||||
|
||||
import android.app.job.JobParameters
|
||||
import android.app.job.JobService
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.media.tv.TvContract
|
||||
import android.util.Log
|
||||
import androidx.tvprovider.media.tv.TvContractCompat
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
import org.dolphinemu.dolphinemu.model.GameFile
|
||||
import org.dolphinemu.dolphinemu.ui.platform.PlatformTab
|
||||
import org.dolphinemu.dolphinemu.utils.AppLinkHelper.buildBrowseUri
|
||||
import org.dolphinemu.dolphinemu.utils.AppLinkHelper.buildGameUri
|
||||
import org.dolphinemu.dolphinemu.utils.TvUtil
|
||||
|
||||
class SyncProgramsJobService : JobService() {
|
||||
private val syncProgramsScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
|
||||
private var syncProgramsJob: Job? = null
|
||||
|
||||
override fun onStartJob(jobParameters: JobParameters): Boolean {
|
||||
Log.d(TAG, "onStartJob(): $jobParameters")
|
||||
val channelId = getChannelId(jobParameters)
|
||||
if (channelId == -1L) {
|
||||
Log.d(TAG, "Failed to find channel")
|
||||
return false
|
||||
}
|
||||
|
||||
syncProgramsJob?.cancel()
|
||||
syncProgramsJob = syncProgramsScope.launch {
|
||||
val finished = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
SyncProgramsTask(applicationContext).run(channelId)
|
||||
}
|
||||
} catch (_: CancellationException) {
|
||||
return@launch
|
||||
} catch (e: RuntimeException) {
|
||||
Log.e(TAG, "Failed to sync programs", e)
|
||||
false
|
||||
}
|
||||
|
||||
syncProgramsJob = null
|
||||
jobFinished(jobParameters, !finished)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onStopJob(jobParameters: JobParameters): Boolean {
|
||||
syncProgramsJob?.cancel()
|
||||
syncProgramsJob = null
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
syncProgramsScope.cancel()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun getChannelId(jobParameters: JobParameters): Long {
|
||||
val extras = jobParameters.extras
|
||||
return extras.getLong(TvContractCompat.EXTRA_CHANNEL_ID, -1L)
|
||||
}
|
||||
|
||||
private class SyncProgramsTask(private val context: Context) {
|
||||
private var updatePrograms: List<GameFile> = emptyList()
|
||||
|
||||
/**
|
||||
* Determines which channel to update, get the game files for the channel,
|
||||
* then updates the list
|
||||
*/
|
||||
suspend fun run(vararg channelIds: Long): Boolean {
|
||||
for (channelId in channelIds) {
|
||||
currentCoroutineContext().ensureActive()
|
||||
|
||||
val channel = TvUtil.getChannelById(context, channelId)
|
||||
for (platformTab in PlatformTab.entries) {
|
||||
if (channel?.appLinkIntentUri == buildBrowseUri(platformTab)) {
|
||||
updatePrograms =
|
||||
GameFileCacheManager.getGameFilesForPlatformTab(platformTab)
|
||||
if (!syncPrograms(channelId)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun syncPrograms(channelId: Long): Boolean {
|
||||
Log.d(TAG, "Sync programs for channel: $channelId")
|
||||
deletePrograms(channelId)
|
||||
return createPrograms(channelId)
|
||||
}
|
||||
|
||||
private suspend fun createPrograms(channelId: Long): Boolean {
|
||||
for (game in updatePrograms) {
|
||||
currentCoroutineContext().ensureActive()
|
||||
|
||||
val previewProgram = buildProgram(channelId, game)
|
||||
context.contentResolver.insert(
|
||||
TvContractCompat.PreviewPrograms.CONTENT_URI, previewProgram
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun deletePrograms(channelId: Long) {
|
||||
context.contentResolver.delete(
|
||||
TvContractCompat.buildPreviewProgramsUriForChannel(channelId), null, null
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildProgram(channelId: Long, game: GameFile): ContentValues {
|
||||
val appLinkUri = buildGameUri(channelId, game.getGameId())
|
||||
var banner = TvUtil.buildBanner(game, context)
|
||||
if (banner == null) {
|
||||
banner = TvUtil.getUriToResource(context, R.drawable.banner_tv)
|
||||
}
|
||||
|
||||
val values = ContentValues()
|
||||
values.put(TvContractCompat.PreviewPrograms.COLUMN_CHANNEL_ID, channelId)
|
||||
values.put(
|
||||
TvContractCompat.PreviewPrograms.COLUMN_TYPE,
|
||||
TvContractCompat.PreviewProgramColumns.TYPE_GAME
|
||||
)
|
||||
values.put(TvContract.Programs.COLUMN_TITLE, game.getTitle())
|
||||
values.put(TvContract.Programs.COLUMN_SHORT_DESCRIPTION, game.getDescription())
|
||||
values.put(TvContract.Programs.COLUMN_POSTER_ART_URI, banner.toString())
|
||||
values.put(
|
||||
TvContractCompat.PreviewPrograms.COLUMN_POSTER_ART_ASPECT_RATIO,
|
||||
TvContractCompat.PreviewPrograms.ASPECT_RATIO_2_3
|
||||
)
|
||||
values.put(TvContractCompat.PreviewPrograms.COLUMN_INTENT_URI, appLinkUri.toString())
|
||||
return values
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SyncProgramsJobService"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user