mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 06:17:57 +01:00 
			
		
		
		
	Application can now check if update available
This commit is contained in:
		| @@ -23,6 +23,10 @@ public class App extends Application { | ||||
|     AppComponent applicationComponent; | ||||
|     ComponentReflectionInjector<AppComponent> componentInjector; | ||||
|  | ||||
|     public static App get(Context context) { | ||||
|         return (App) context.getApplicationContext(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate() { | ||||
|         super.onCreate(); | ||||
| @@ -38,20 +42,16 @@ public class App extends Application { | ||||
|         ACRA.init(this); | ||||
|     } | ||||
|  | ||||
|     public static App get(Context context) { | ||||
|         return (App) context.getApplicationContext(); | ||||
|     } | ||||
|  | ||||
|     public AppComponent getComponent() { | ||||
|         return applicationComponent; | ||||
|     } | ||||
|  | ||||
|     public ComponentReflectionInjector<AppComponent> getComponentReflection() { | ||||
|         return componentInjector; | ||||
|     } | ||||
|  | ||||
|     // Needed to replace the component with a test specific one | ||||
|     public void setComponent(AppComponent applicationComponent) { | ||||
|         this.applicationComponent = applicationComponent; | ||||
|     } | ||||
|  | ||||
|     public ComponentReflectionInjector<AppComponent> getComponentReflection() { | ||||
|         return componentInjector; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,15 @@ | ||||
| package eu.kanade.tachiyomi.data.rest; | ||||
|  | ||||
| import retrofit.http.GET; | ||||
| import rx.Observable; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Used to connect with the Github API | ||||
|  */ | ||||
| public interface GithubService { | ||||
|     String SERVICE_ENDPOINT = "https://api.github.com"; | ||||
|  | ||||
|     @GET("/repos/inorichi/tachiyomi/releases/latest") Observable<Release> getLatestVersion(); | ||||
|  | ||||
| } | ||||
							
								
								
									
										93
									
								
								app/src/main/java/eu/kanade/tachiyomi/data/rest/Release.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								app/src/main/java/eu/kanade/tachiyomi/data/rest/Release.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| package eu.kanade.tachiyomi.data.rest; | ||||
|  | ||||
| import com.google.gson.annotations.SerializedName; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * Release object | ||||
|  * Contains information about the latest release | ||||
|  */ | ||||
| public class Release { | ||||
|     /** | ||||
|      * Version name V0.0.0 | ||||
|      */ | ||||
|     @SerializedName("tag_name") | ||||
|     private final String version; | ||||
|  | ||||
|     /** Change Log */ | ||||
|     @SerializedName("body") | ||||
|     private final String log; | ||||
|  | ||||
|     /** Assets containing download url */ | ||||
|     @SerializedName("assets") | ||||
|     private final List<Assets> assets; | ||||
|  | ||||
|     /** | ||||
|      * Release constructor | ||||
|      * | ||||
|      * @param version version of latest release | ||||
|      * @param log     log of latest release | ||||
|      * @param assets  assets of latest release | ||||
|      */ | ||||
|     public Release(String version, String log, List<Assets> assets) { | ||||
|         this.version = version; | ||||
|         this.log = log; | ||||
|         this.assets = assets; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get latest release version | ||||
|      * | ||||
|      * @return latest release version | ||||
|      */ | ||||
|     public String getVersion() { | ||||
|         return version; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get change log of latest release | ||||
|      * | ||||
|      * @return change log of latest release | ||||
|      */ | ||||
|     public String getChangeLog() { | ||||
|         return log; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get download link of latest release | ||||
|      * | ||||
|      * @return download link of latest release | ||||
|      */ | ||||
|     public String getDownloadLink() { | ||||
|         return assets.get(0).getDownloadLink(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Assets class containing download url | ||||
|      */ | ||||
|     class Assets { | ||||
|         @SerializedName("browser_download_url") | ||||
|         private final String download_url; | ||||
|  | ||||
|  | ||||
|         /** | ||||
|          * Assets Constructor | ||||
|          * | ||||
|          * @param download_url download url | ||||
|          */ | ||||
|         @SuppressWarnings("unused") public Assets(String download_url) { | ||||
|             this.download_url = download_url; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Get download link of latest release | ||||
|          * | ||||
|          * @return download link of latest release | ||||
|          */ | ||||
|         public String getDownloadLink() { | ||||
|             return download_url; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,21 @@ | ||||
| package eu.kanade.tachiyomi.data.rest; | ||||
|  | ||||
| import retrofit.RestAdapter; | ||||
|  | ||||
| public class ServiceFactory { | ||||
|  | ||||
|     /** | ||||
|      * Creates a retrofit service from an arbitrary class (clazz) | ||||
|      * | ||||
|      * @param clazz    Java interface of the retrofit service | ||||
|      * @param endPoint REST endpoint url | ||||
|      * @return retrofit service with defined endpoint | ||||
|      */ | ||||
|     public static <T> T createRetrofitService(final Class<T> clazz, final String endPoint) { | ||||
|         final RestAdapter restAdapter = new RestAdapter.Builder() | ||||
|                 .setEndpoint(endPoint) | ||||
|                 .build(); | ||||
|  | ||||
|         return restAdapter.create(clazz); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,31 @@ | ||||
| package eu.kanade.tachiyomi.data.updater; | ||||
|  | ||||
|  | ||||
| import android.content.Context; | ||||
|  | ||||
| import eu.kanade.tachiyomi.R; | ||||
| import eu.kanade.tachiyomi.data.rest.GithubService; | ||||
| import eu.kanade.tachiyomi.data.rest.Release; | ||||
| import eu.kanade.tachiyomi.data.rest.ServiceFactory; | ||||
| import eu.kanade.tachiyomi.util.ToastUtil; | ||||
| import rx.Observable; | ||||
|  | ||||
|  | ||||
| public class UpdateChecker { | ||||
|     private final Context context; | ||||
|  | ||||
|     public UpdateChecker(Context context) { | ||||
|         this.context = context; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns observable containing release information | ||||
|      * | ||||
|      */ | ||||
|     public Observable<Release> checkForApplicationUpdate() { | ||||
|         ToastUtil.showShort(context, context.getString(R.string.update_check_look_for_updates)); | ||||
|         //Create Github service to retrieve Github data | ||||
|         GithubService service = ServiceFactory.createRetrofitService(GithubService.class, GithubService.SERVICE_ENDPOINT); | ||||
|         return service.getLatestVersion(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,99 @@ | ||||
| package eu.kanade.tachiyomi.data.updater; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.net.Uri; | ||||
| import android.os.AsyncTask; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.InputStream; | ||||
| import java.net.HttpURLConnection; | ||||
| import java.net.URL; | ||||
|  | ||||
| import javax.inject.Inject; | ||||
|  | ||||
| import eu.kanade.tachiyomi.App; | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper; | ||||
|  | ||||
| public class UpdateDownloader extends AsyncTask<String, Void, Void> { | ||||
|     /** | ||||
|      * Name of cache directory. | ||||
|      */ | ||||
|     private static final String PARAMETER_CACHE_DIRECTORY = "apk_downloads"; | ||||
|     /** | ||||
|      * Interface to global information about an application environment. | ||||
|      */ | ||||
|     private final Context context; | ||||
|     /** | ||||
|      * Cache directory used for cache management. | ||||
|      */ | ||||
|     private final File cacheDir; | ||||
|     @Inject PreferencesHelper preferencesHelper; | ||||
|  | ||||
|     /** | ||||
|      * Constructor of UpdaterCache. | ||||
|      * | ||||
|      * @param context application environment interface. | ||||
|      */ | ||||
|     public UpdateDownloader(Context context) { | ||||
|         App.get(context).getComponent().inject(this); | ||||
|         this.context = context; | ||||
|  | ||||
|         // Get cache directory from parameter. | ||||
|         cacheDir = new File(preferencesHelper.getDownloadsDirectory(), PARAMETER_CACHE_DIRECTORY); | ||||
|  | ||||
|         // Create cache directory. | ||||
|         createCacheDir(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create cache directory if it doesn't exist | ||||
|      * | ||||
|      * @return true if cache dir is created otherwise false. | ||||
|      */ | ||||
|     @SuppressWarnings("UnusedReturnValue") | ||||
|     private boolean createCacheDir() { | ||||
|         return !cacheDir.exists() && cacheDir.mkdirs(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @Override | ||||
|     protected Void doInBackground(String... arg0) { | ||||
|         try { | ||||
|             createCacheDir(); | ||||
|  | ||||
|             URL url = new URL(arg0[0]); | ||||
|             HttpURLConnection c = (HttpURLConnection) url.openConnection(); | ||||
|             c.connect(); | ||||
|  | ||||
|             File outputFile = new File(cacheDir, "update.apk"); | ||||
|             if (outputFile.exists()) { | ||||
|                 //noinspection ResultOfMethodCallIgnored | ||||
|                 outputFile.delete(); | ||||
|             } | ||||
|             FileOutputStream fos = new FileOutputStream(outputFile); | ||||
|  | ||||
|             InputStream is = c.getInputStream(); | ||||
|  | ||||
|             byte[] buffer = new byte[1024]; | ||||
|             int len1; | ||||
|             while ((len1 = is.read(buffer)) != -1) { | ||||
|                 fos.write(buffer, 0, len1); | ||||
|             } | ||||
|             fos.close(); | ||||
|             is.close(); | ||||
|  | ||||
|             // Prompt install interface | ||||
|             Intent intent = new Intent(Intent.ACTION_VIEW); | ||||
|             intent.setDataAndType(Uri.fromFile(outputFile), "application/vnd.android.package-archive"); | ||||
|             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // without this flag android returned a intent error! | ||||
|             context.startActivity(intent); | ||||
|  | ||||
|         } catch (Exception e) { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.data.mangasync.services.MyAnimeList; | ||||
| import eu.kanade.tachiyomi.data.source.base.Source; | ||||
| import eu.kanade.tachiyomi.data.sync.LibraryUpdateService; | ||||
| import eu.kanade.tachiyomi.data.sync.UpdateMangaSyncService; | ||||
| import eu.kanade.tachiyomi.data.updater.UpdateDownloader; | ||||
| import eu.kanade.tachiyomi.injection.module.AppModule; | ||||
| import eu.kanade.tachiyomi.injection.module.DataModule; | ||||
| import eu.kanade.tachiyomi.ui.catalogue.CataloguePresenter; | ||||
| @@ -50,6 +51,7 @@ public interface AppComponent { | ||||
|     void inject(ReaderActivity readerActivity); | ||||
|     void inject(MangaActivity mangaActivity); | ||||
|     void inject(SettingsAccountsFragment settingsAccountsFragment); | ||||
|  | ||||
|     void inject(SettingsActivity settingsActivity); | ||||
|  | ||||
|     void inject(Source source); | ||||
| @@ -59,6 +61,8 @@ public interface AppComponent { | ||||
|     void inject(LibraryUpdateService libraryUpdateService); | ||||
|     void inject(DownloadService downloadService); | ||||
|     void inject(UpdateMangaSyncService updateMangaSyncService); | ||||
|  | ||||
|     void inject(UpdateDownloader updateDownloader); | ||||
|     Application application(); | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -6,6 +6,8 @@ import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
|  | ||||
| import com.afollestad.materialdialogs.MaterialDialog; | ||||
|  | ||||
| import java.text.DateFormat; | ||||
| import java.text.ParseException; | ||||
| import java.text.SimpleDateFormat; | ||||
| @@ -15,8 +17,23 @@ import java.util.TimeZone; | ||||
|  | ||||
| import eu.kanade.tachiyomi.BuildConfig; | ||||
| import eu.kanade.tachiyomi.R; | ||||
| import eu.kanade.tachiyomi.data.updater.UpdateChecker; | ||||
| import eu.kanade.tachiyomi.data.updater.UpdateDownloader; | ||||
| import eu.kanade.tachiyomi.util.ToastUtil; | ||||
| import rx.Subscription; | ||||
| import rx.android.schedulers.AndroidSchedulers; | ||||
| import rx.schedulers.Schedulers; | ||||
|  | ||||
| public class SettingsAboutFragment extends SettingsNestedFragment { | ||||
|     /** | ||||
|      * Checks for new releases | ||||
|      */ | ||||
|     private UpdateChecker updateChecker; | ||||
|  | ||||
|     /** | ||||
|      * The subscribtion service of the obtained release object | ||||
|      */ | ||||
|     private Subscription releaseSubscription; | ||||
|  | ||||
|     public static SettingsNestedFragment newInstance(int resourcePreference, int resourceTitle) { | ||||
|         SettingsNestedFragment fragment = new SettingsAboutFragment(); | ||||
| @@ -25,14 +42,36 @@ public class SettingsAboutFragment extends SettingsNestedFragment { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) { | ||||
|     public void onCreate(Bundle savedInstanceState) { | ||||
|         //Check for update | ||||
|         updateChecker = new UpdateChecker(getActivity()); | ||||
|  | ||||
|         super.onCreate(savedInstanceState); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         if (releaseSubscription != null) | ||||
|             releaseSubscription.unsubscribe(); | ||||
|  | ||||
|         super.onDestroyView(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) { | ||||
|         Preference version = findPreference(getString(R.string.pref_version)); | ||||
|         Preference buildTime = findPreference(getString(R.string.pref_build_time)); | ||||
|  | ||||
|         version.setSummary(BuildConfig.DEBUG ? "r" + BuildConfig.COMMIT_COUNT : | ||||
|                 BuildConfig.VERSION_NAME); | ||||
|  | ||||
|         //Set onClickListener to check for new version | ||||
|         version.setOnPreferenceClickListener(preference -> { | ||||
|             if (!BuildConfig.DEBUG) | ||||
|                 checkVersion(); | ||||
|             return true; | ||||
|         }); | ||||
|  | ||||
|         buildTime.setSummary(getFormattedBuildTime()); | ||||
|  | ||||
|         return super.onCreateView(inflater, container, savedState); | ||||
| @@ -54,4 +93,41 @@ public class SettingsAboutFragment extends SettingsNestedFragment { | ||||
|         } | ||||
|         return ""; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Checks version and shows a user prompt when update available. | ||||
|      */ | ||||
|     private void checkVersion() { | ||||
|         releaseSubscription = updateChecker.checkForApplicationUpdate() | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe(next -> { | ||||
|                             //Get version of latest releaseSubscription | ||||
|                             String newVersion = next.getVersion(); | ||||
|                             newVersion = newVersion.replaceAll("[^\\d.]", ""); | ||||
|  | ||||
|                             //Check if latest version is different from current version | ||||
|                             if (!newVersion.equals(BuildConfig.VERSION_NAME)) { | ||||
|                                 String downloadLink = next.getDownloadLink(); | ||||
|                                 String body = next.getChangeLog(); | ||||
|  | ||||
|                                 //Create confirmation window | ||||
|                                 new MaterialDialog.Builder(getActivity()) | ||||
|                                         .title(getString(R.string.update_check_title)) | ||||
|                                         .content(body) | ||||
|                                         .positiveText(getString(R.string.update_check_confirm)) | ||||
|                                         .negativeText(getString(R.string.update_check_ignore)) | ||||
|                                         .onPositive((dialog, which) -> { | ||||
|                                             // User output that download has started | ||||
|                                             ToastUtil.showShort(getActivity(), getString(R.string.update_check_download_started)); | ||||
|                                             // Start download | ||||
|                                             new UpdateDownloader(getActivity()).execute(downloadLink); | ||||
|                                         }) | ||||
|                                         .show(); | ||||
|                             } else { | ||||
|                                 ToastUtil.showShort(getActivity(), getString(R.string.update_check_no_new_updates)); | ||||
|                             } | ||||
|                         }, | ||||
|                         Throwable::printStackTrace); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user