Java tutorial
/* * Copyright (C) 2014 Michell Bak * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.miz.service; import android.app.IntentService; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.database.Cursor; import android.graphics.Bitmap; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import com.miz.abstractclasses.TvShowFileSource; import com.miz.apis.trakt.Trakt; import com.miz.db.DbAdapterSources; import com.miz.db.DbAdapterTvShowEpisodes; import com.miz.db.DbAdapterTvShows; import com.miz.filesources.FileTvShow; import com.miz.filesources.SmbTvShow; import com.miz.filesources.UpnpTvShow; import com.miz.functions.FileSource; import com.miz.functions.MizLib; import com.miz.functions.TvShowLibraryUpdateCallback; import com.miz.identification.ShowStructure; import com.miz.identification.TvShowIdentification; import com.miz.mizuu.CancelLibraryUpdate; import com.miz.mizuu.Main; import com.miz.mizuu.MizuuApplication; import com.miz.mizuu.R; import com.miz.utils.FileUtils; import com.miz.utils.LocalBroadcastUtils; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import static com.miz.functions.PreferenceKeys.CLEAR_LIBRARY_TVSHOWS; import static com.miz.functions.PreferenceKeys.REMOVE_UNAVAILABLE_FILES_TVSHOWS; import static com.miz.functions.PreferenceKeys.SYNC_WITH_TRAKT; public class TvShowsLibraryUpdate extends IntentService implements TvShowLibraryUpdateCallback { public static final String STOP_TVSHOW_LIBRARY_UPDATE = "mizuu-stop-tvshow-library-update"; private boolean mDebugging = true; private ArrayList<FileSource> mFileSources; private ArrayList<TvShowFileSource<?>> mTvShowFileSources; private ArrayList<ShowStructure> mFiles; private HashSet<String> mUniqueShowIds = new HashSet<String>(); private boolean mClearLibrary, mClearUnavailable, mSyncLibraries, mStopUpdate; private int mTotalFiles, mShowCount, mEpisodeCount; private SharedPreferences mSettings; private Editor mEditor; private final int NOTIFICATION_ID = 300, POST_UPDATE_NOTIFICATION = 313; private NotificationManager mNotificationManager; private NotificationCompat.Builder mBuilder; private TvShowIdentification mIdentification; public TvShowsLibraryUpdate() { super("TvShowsLibraryUpdate"); } public TvShowsLibraryUpdate(String name) { super(name); } @Override public void onDestroy() { super.onDestroy(); log("onDestroy()"); if (mNotificationManager == null) mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager.cancel(NOTIFICATION_ID); LocalBroadcastUtils.updateTvShowLibrary(this); showPostUpdateNotification(); LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver); MizLib.scheduleShowsUpdate(this); if (Trakt.hasTraktAccount(this) && mSyncLibraries && (mEpisodeCount > 0)) { startService(new Intent(getApplicationContext(), TraktTvShowsSyncService.class)); } } @Override protected void onHandleIntent(Intent intent) { log("clear()"); // Clear and set up all variables clear(); log("setup()"); // Set up Notification, variables, etc. setup(); log("loadFileSources()"); // Load all file sources from the database loadFileSources(); log("setupTvShowsFileSources()"); // Add the different file sources to the TvShowFileSource ArrayList setupTvShowsFileSources(mClearLibrary); if (mStopUpdate) return; log("removeUnidentifiedFiles()"); // Remove unavailable TV show files, so we can try to identify them again if (!mClearLibrary) removeUnidentifiedFiles(); if (mStopUpdate) return; // Check if the library should be cleared if (mClearLibrary) { // Reset the preference, so it isn't checked the next // time the user wants to update the library mEditor = mSettings.edit(); mEditor.putBoolean(CLEAR_LIBRARY_TVSHOWS, false); mEditor.apply(); log("removeTvShowsFromDatabase()"); // Remove all entries from the database removeTvShowsFromDatabase(); } if (mStopUpdate) return; // Check if we should remove all unavailable files. // Note that this only makes sense if we haven't already cleared the library. if (!mClearLibrary && mClearUnavailable) { log("removeUnavailableFiles()"); // Remove all unavailable files from the database removeUnavailableFiles(); } log("searchFolders()"); if (mStopUpdate) return; // Search all folders searchFolders(); if (mStopUpdate) return; log("mTotalFiles > 0 check"); // Check if we've found any files to identify if (mTotalFiles > 0) { log("updateTvShows()"); // Start the actual TV shows update / identification task updateTvShows(); } } private void loadFileSources() { mFileSources = new ArrayList<FileSource>(); DbAdapterSources dbHelperSources = MizuuApplication.getSourcesAdapter(); Cursor c = dbHelperSources.fetchAllShowSources(); try { while (c.moveToNext()) { mFileSources.add(new FileSource(c.getLong(c.getColumnIndex(DbAdapterSources.KEY_ROWID)), c.getString(c.getColumnIndex(DbAdapterSources.KEY_FILEPATH)), c.getInt(c.getColumnIndex(DbAdapterSources.KEY_FILESOURCE_TYPE)), c.getString(c.getColumnIndex(DbAdapterSources.KEY_USER)), c.getString(c.getColumnIndex(DbAdapterSources.KEY_PASSWORD)), c.getString(c.getColumnIndex(DbAdapterSources.KEY_DOMAIN)), c.getString(c.getColumnIndex(DbAdapterSources.KEY_TYPE)))); } } catch (Exception e) { } finally { c.close(); } } private void setupTvShowsFileSources(boolean mClearLibrary) { for (FileSource fileSource : mFileSources) { if (mStopUpdate) return; switch (fileSource.getFileSourceType()) { case FileSource.FILE: mTvShowFileSources.add(new FileTvShow(getApplicationContext(), fileSource, mClearLibrary)); break; case FileSource.SMB: mTvShowFileSources.add(new SmbTvShow(getApplicationContext(), fileSource, mClearLibrary)); break; case FileSource.UPNP: mTvShowFileSources.add(new UpnpTvShow(getApplicationContext(), fileSource, mClearLibrary)); break; } } } private void removeUnidentifiedFiles() { for (TvShowFileSource<?> tvShowFileSource : mTvShowFileSources) { tvShowFileSource.removeUnidentifiedFiles(); } } private void removeTvShowsFromDatabase() { // Delete all shows from the database DbAdapterTvShows db = MizuuApplication.getTvDbAdapter(); db.deleteAllShowsInDatabase(); DbAdapterTvShowEpisodes dbEpisodes = MizuuApplication.getTvEpisodeDbAdapter(); dbEpisodes.deleteAllEpisodes(); MizuuApplication.getTvShowEpisodeMappingsDbAdapter().deleteAllFilepaths(); // Delete all downloaded images files from the device FileUtils.deleteRecursive(MizuuApplication.getTvShowThumbFolder(this), false); FileUtils.deleteRecursive(MizuuApplication.getTvShowEpisodeFolder(this), false); FileUtils.deleteRecursive(MizuuApplication.getTvShowBackdropFolder(this), false); FileUtils.deleteRecursive(MizuuApplication.getTvShowSeasonFolder(this), false); } private void removeUnavailableFiles() { for (TvShowFileSource<?> tvShowFileSource : mTvShowFileSources) { tvShowFileSource.removeUnavailableFiles(); } } private void searchFolders() { // Temporary collection List<String> tempList = null; for (int j = 0; j < mTvShowFileSources.size(); j++) { updateTvShowScanningNotification(mTvShowFileSources.get(j).toString()); tempList = mTvShowFileSources.get(j).searchFolder(); for (int i = 0; i < tempList.size(); i++) { mFiles.add(new ShowStructure(tempList.get(i))); } } // Clean up... if (tempList != null) tempList.clear(); int episodeCount = 0; for (ShowStructure ss : mFiles) episodeCount += ss.getEpisodes().size(); mTotalFiles = episodeCount; } private void setup() { if (!MizLib.isOnline(this)) { mStopUpdate = true; return; } LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(mMessageReceiver, new IntentFilter(STOP_TVSHOW_LIBRARY_UPDATE)); // Set up cancel dialog intent Intent notificationIntent = new Intent(this, CancelLibraryUpdate.class); notificationIntent.putExtra("isMovie", false); notificationIntent.setAction(Intent.ACTION_MAIN); notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER); PendingIntent contentIntent = PendingIntent.getActivity(this, NOTIFICATION_ID, notificationIntent, 0); // Setup up notification mBuilder = new NotificationCompat.Builder(getApplicationContext()); mBuilder.setColor(getResources().getColor(R.color.color_primary)); mBuilder.setSmallIcon(R.drawable.ic_sync_white_24dp); mBuilder.setTicker(getString(R.string.updatingTvShows)); mBuilder.setContentTitle(getString(R.string.updatingTvShows)); mBuilder.setContentText(getString(R.string.gettingReady)); mBuilder.setContentIntent(contentIntent); mBuilder.setOngoing(true); mBuilder.setOnlyAlertOnce(true); mBuilder.addAction(R.drawable.ic_close_white_24dp, getString(android.R.string.cancel), contentIntent); // Build notification Notification updateNotification = mBuilder.build(); // Show the notification mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager.notify(NOTIFICATION_ID, updateNotification); // Tell the system that this is an ongoing notification, so it shouldn't be killed startForeground(NOTIFICATION_ID, updateNotification); mSettings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); mClearLibrary = mSettings.getBoolean(CLEAR_LIBRARY_TVSHOWS, false); mClearUnavailable = mSettings.getBoolean(REMOVE_UNAVAILABLE_FILES_TVSHOWS, false); mSyncLibraries = mSettings.getBoolean(SYNC_WITH_TRAKT, true); } private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { mStopUpdate = true; if (mIdentification != null) mIdentification.cancel(); } }; private void updateTvShows() { // Show the "Analyzing files..." notification showTvShowAnalyzingNotification(); mIdentification = new TvShowIdentification(getApplicationContext(), this, mFiles); mIdentification.start(); } private void clear() { // Lists mFileSources = new ArrayList<FileSource>(); mTvShowFileSources = new ArrayList<TvShowFileSource<?>>(); mFiles = new ArrayList<ShowStructure>(); mUniqueShowIds = new HashSet<String>(); // Booleans mClearLibrary = false; mClearUnavailable = false; mSyncLibraries = true; mStopUpdate = false; // Other variables mEditor = null; mSettings = null; mTotalFiles = 0; mShowCount = 0; mNotificationManager = null; mBuilder = null; } private void log(String msg) { if (mDebugging) Log.d("TvShowsLibraryUpdate", msg); } @Override public void onTvShowAdded(String showId, String title, Bitmap cover, Bitmap backdrop, int count) { if (!showId.equals(DbAdapterTvShows.UNIDENTIFIED_ID)) { mUniqueShowIds.add(showId); } updateTvShowAddedNotification(showId, title, cover, backdrop, count); } @Override public void onEpisodeAdded(String showId, String title, Bitmap cover, Bitmap photo) { if (!showId.equals(DbAdapterTvShows.UNIDENTIFIED_ID)) mEpisodeCount++; updateEpisodeAddedNotification(showId, title, cover, photo); } private void updateEpisodeAddedNotification(String showId, String title, Bitmap cover, Bitmap backdrop) { String contentText; if (showId.isEmpty() || showId.equalsIgnoreCase(DbAdapterTvShows.UNIDENTIFIED_ID)) contentText = getString(R.string.unidentified) + ": " + title; else contentText = getString(R.string.stringJustAdded) + ": " + title; mBuilder.setLargeIcon(cover); mBuilder.setContentTitle(getString(R.string.updatingTvShows) + " (" + (int) ((100.0 / (double) mTotalFiles) * (double) mEpisodeCount) + "%)"); mBuilder.setContentText(contentText); mBuilder.setStyle( new NotificationCompat.BigPictureStyle().setSummaryText(contentText).bigPicture(backdrop)); // Show the updated notification mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build()); } private void updateTvShowAddedNotification(String showId, String title, Bitmap cover, Bitmap backdrop, int count) { String contentText; if (showId.isEmpty() || showId.equalsIgnoreCase(DbAdapterTvShows.UNIDENTIFIED_ID)) contentText = getString(R.string.unidentified) + ": " + title + " (" + count + " " + getResources().getQuantityString(R.plurals.episodes, count, count) + ")"; else contentText = getString(R.string.stringJustAdded) + ": " + title + " (" + count + " " + getResources().getQuantityString(R.plurals.episodes, count, count) + ")"; mBuilder.setLargeIcon(cover); mBuilder.setContentTitle(getString(R.string.updatingTvShows) + " (" + (int) ((100.0 / (double) mTotalFiles) * (double) mEpisodeCount) + "%)"); mBuilder.setContentText(contentText); mBuilder.setStyle( new NotificationCompat.BigPictureStyle().setSummaryText(contentText).bigPicture(backdrop)); // Show the updated notification mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build()); } private void updateTvShowScanningNotification(String filesource) { mBuilder.setSmallIcon(R.drawable.ic_sync_white_24dp); mBuilder.setContentTitle(getString(R.string.updatingTvShows)); mBuilder.setContentText(getString(R.string.scanning) + ": " + filesource); // Show the updated notification mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build()); } private void showTvShowAnalyzingNotification() { mBuilder.setSmallIcon(R.drawable.ic_sync_white_24dp); mBuilder.setContentTitle(getString(R.string.updatingTvShows)); mBuilder.setContentText(getString(R.string.analyzing_files)); // Show the updated notification mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build()); } private void showPostUpdateNotification() { mShowCount = mUniqueShowIds.size(); // Set up cancel dialog intent Intent notificationIntent = new Intent(this, Main.class); notificationIntent.putExtra("fromUpdate", true); notificationIntent.putExtra("startup", "2"); notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); // Setup up notification mBuilder = new NotificationCompat.Builder(getApplicationContext()); mBuilder.setColor(getResources().getColor(R.color.color_primary)); if (!mStopUpdate) { mBuilder.setSmallIcon(R.drawable.ic_done_white_24dp); mBuilder.setTicker(getString(R.string.finishedTvShowsLibraryUpdate)); mBuilder.setContentTitle(getString(R.string.finishedTvShowsLibraryUpdate)); mBuilder.setContentText(getString(R.string.stringJustAdded) + " " + mShowCount + " " + getResources().getQuantityString(R.plurals.showsInLibrary, mShowCount, mShowCount) + " (" + mEpisodeCount + " " + getResources().getQuantityString(R.plurals.episodes, mEpisodeCount, mEpisodeCount) + ")"); } else { mBuilder.setSmallIcon(R.drawable.ic_cancel_white_24dp); mBuilder.setTicker(getString(R.string.stringUpdateCancelled)); mBuilder.setContentTitle(getString(R.string.stringUpdateCancelled)); mBuilder.setContentText(getString(R.string.stringJustAdded) + " " + mShowCount + " " + getResources().getQuantityString(R.plurals.showsInLibrary, mShowCount, mShowCount) + " (" + mEpisodeCount + " " + getResources().getQuantityString(R.plurals.episodes, mEpisodeCount, mEpisodeCount) + ")"); } mBuilder.setContentIntent(contentIntent); mBuilder.setAutoCancel(true); // Build notification Notification updateNotification = mBuilder.build(); // Show the notification mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (mEpisodeCount > 0) mNotificationManager.notify(POST_UPDATE_NOTIFICATION, updateNotification); } }