/* * Overchan Android (Meta Imageboard Client) * Copyright (C) 2014-2016 miku-nyan <https://github.com/miku-nyan> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package nya.miku.wishmaster.common; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import nya.miku.wishmaster.R; import nya.miku.wishmaster.api.ChanModule; import nya.miku.wishmaster.cache.BitmapCache; import nya.miku.wishmaster.cache.DraftsCache; import nya.miku.wishmaster.cache.FileCache; import nya.miku.wishmaster.cache.PagesCache; import nya.miku.wishmaster.cache.Serializer; import nya.miku.wishmaster.http.SSLCompatibility; import nya.miku.wishmaster.http.client.ExtendedTrustManager; import nya.miku.wishmaster.http.streamer.HttpStreamer; import nya.miku.wishmaster.lib.org_json.JSONArray; import nya.miku.wishmaster.ui.Database; import nya.miku.wishmaster.ui.downloading.DownloadingLocker; import nya.miku.wishmaster.ui.presentation.Subscriptions; import nya.miku.wishmaster.ui.settings.ApplicationSettings; import nya.miku.wishmaster.ui.settings.Wifi; import nya.miku.wishmaster.ui.tabs.TabsState; import nya.miku.wishmaster.ui.tabs.TabsSwitcher; import org.acra.ACRA; import org.acra.annotation.ReportsCrashes; import android.app.ActivityManager; import android.app.ActivityManager.RunningAppProcessInfo; import android.app.Application; import android.content.SharedPreferences; import android.content.res.Resources; import android.preference.PreferenceManager; /** * Класс приложения (расширяет {@link Application}).<br> * Экземпляр ({@link #getInstance()) хранит объекты, используемые в различных частях проекта. * @author miku-nyan * */ @ReportsCrashes( formUri = ACRAConstants.ACRA_FORM_URL, reportType = org.acra.sender.HttpSender.Type.JSON, httpMethod = org.acra.sender.HttpSender.Method.PUT, formUriBasicAuthLogin = ACRAConstants.ACRA_LOGIN, formUriBasicAuthPassword = ACRAConstants.ACRA_PASSWORD, mode = org.acra.ReportingInteractionMode.DIALOG, resDialogText = R.string.crash_dialog_text, resDialogIcon = android.R.drawable.ic_dialog_info, resDialogTitle = R.string.crash_dialog_title, resDialogCommentPrompt = R.string.crash_dialog_comment_prompt, resDialogOkToast = R.string.crash_dialog_ok_toast ) public class MainApplication extends Application { private static final String[] MODULES = new String[] { "nya.miku.wishmaster.chans.fourchan.FourchanModule", "nya.miku.wishmaster.chans.krautchan.KrautModule", "nya.miku.wishmaster.chans.infinity.InfinityModule", "nya.miku.wishmaster.chans.cirno.CirnoModule", "nya.miku.wishmaster.chans.cirno.MikubaModule", "nya.miku.wishmaster.chans.dobrochan.DobroModule", "nya.miku.wishmaster.chans.dvach.DvachModule", "nya.miku.wishmaster.chans.sevenchan.SevenchanModule", "nya.miku.wishmaster.chans.wakachan.WakachanModule", "nya.miku.wishmaster.chans.infinity.InfinityPlModule", "nya.miku.wishmaster.chans.cirno.NowereModule", "nya.miku.wishmaster.chans.cirno.Chan410Module", "nya.miku.wishmaster.chans.chan420.Chan420Module", "nya.miku.wishmaster.chans.cirno.OwlchanModule", "nya.miku.wishmaster.chans.horochan.HorochanModule", "nya.miku.wishmaster.chans.allchan.AllchanModule", "nya.miku.wishmaster.chans.ponyach.PonyachModule", "nya.miku.wishmaster.chans.uchan.UchanModule", "nya.miku.wishmaster.chans.sich.SichModule", "nya.miku.wishmaster.chans.nullchan.NullchanccModule", "nya.miku.wishmaster.chans.nullchan.NullchaneuModule", "nya.miku.wishmaster.chans.nullchan.Null_chanModule", "nya.miku.wishmaster.chans.dvachnet.DvachnetModule", "nya.miku.wishmaster.chans.mentachsu.MentachsuModule", "nya.miku.wishmaster.chans.synch.SynchModule", "nya.miku.wishmaster.chans.inach.InachModule", "nya.miku.wishmaster.chans.clairews.ClairewsModule", "nya.miku.wishmaster.chans.kurisach.KurisachModule", "nya.miku.wishmaster.chans.chan10.Chan10Module", "nya.miku.wishmaster.chans.haruhichan.HaruhiModule", "nya.miku.wishmaster.chans.vichan.VichanModule", "nya.miku.wishmaster.chans.lainchan.LainModule", "nya.miku.wishmaster.chans.tohnochan.TohnoChanModule", "nya.miku.wishmaster.chans.chan76.Chan76Module", "nya.miku.wishmaster.chans.dfwk.DFWKModule", "nya.miku.wishmaster.chans.anonfm.AnonFmModule", "nya.miku.wishmaster.chans.makaba.MakabaModule", "nya.miku.wishmaster.chans.arhivach.ArhivachModule", "nya.miku.wishmaster.chans.samachan.SamachanModule", }; private static MainApplication instance; public static MainApplication getInstance() { if (instance == null) throw new IllegalStateException("Must be called after onCreate()"); return instance; } public SharedPreferences preferences; public Resources resources; public ApplicationSettings settings; public FileCache fileCache; public Serializer serializer; public BitmapCache bitmapCache; public PagesCache pagesCache; public DraftsCache draftsCache; public Database database; public Subscriptions subscriptions; public DownloadingLocker downloadingLocker; public TabsState tabsState; public TabsSwitcher tabsSwitcher; public List<ChanModule> chanModulesList; private Map<String, Integer> chanModulesIndex; private void registerChanModules() { chanModulesIndex = new HashMap<String, Integer>(); chanModulesList = new ArrayList<ChanModule>(); registerChanModules(chanModulesList, chanModulesIndex); } public void updateChanModulesOrder() { Map<String, ChanModule> instantiatedMap = new HashMap<>(); for (ChanModule chan : chanModulesList) instantiatedMap.put(chan.getClass().getName(), chan); Map<String, Integer> indexMap = new HashMap<>(); List<ChanModule> list = new ArrayList<>(); registerChanModules(list, indexMap, instantiatedMap); chanModulesIndex = indexMap; chanModulesList = list; } private void registerChanModules(List<ChanModule> outList, Map<String, Integer> outIndexMap) { registerChanModules(outList, outIndexMap, null); } private void registerChanModules(List<ChanModule> outList, Map<String, Integer> outIndexMap, Map<String, ChanModule> instantiatedClassMap) { Set<String> added = new HashSet<>(); JSONArray order; try { order = new JSONArray(settings.getChansOrderJson()); } catch (Exception e) { order = new JSONArray(); } for (int i=0; i<order.length(); ++i) { String module = order.optString(i); if (!added.contains(module)) { if (instantiatedClassMap != null && instantiatedClassMap.containsKey(module)) { addChanModule(instantiatedClassMap.get(module), outList, outIndexMap); } else { addChanModule(module, outList, outIndexMap); } } added.add(module); } for (String module : MODULES) { if (!added.contains(module)) { if (instantiatedClassMap != null && instantiatedClassMap.containsKey(module)) { addChanModule(instantiatedClassMap.get(module), outList, outIndexMap); } else { addChanModule(module, outList, outIndexMap); } } added.add(module); } } private void addChanModule(String className, List<ChanModule> list, Map<String, Integer> indexMap) { try { Class<?> c = Class.forName(className); addChanModule((ChanModule) c.getConstructor(SharedPreferences.class, Resources.class).newInstance(preferences, resources), list, indexMap); } catch (Exception e) {} } private void addChanModule(ChanModule module, List<ChanModule> list, Map<String, Integer> indexMap) { indexMap.put(module.getChanName(), list.size()); list.add(module); } public ChanModule getChanModule(String chanName) { if (!chanModulesIndex.containsKey(chanName)) return null; return chanModulesList.get(chanModulesIndex.get(chanName).intValue()); } private void initObjects() { SSLCompatibility.fixSSLs(this); ExtendedTrustManager.setAppContext(this); HttpStreamer.initInstance(); preferences = PreferenceManager.getDefaultSharedPreferences(this); resources = this.getResources(); settings = new ApplicationSettings(preferences, resources); fileCache = new FileCache(this, settings.getMaxCacheSize()); serializer = new Serializer(fileCache); tabsState = serializer.deserializeTabsState(); tabsSwitcher = new TabsSwitcher(); long maxHeapSize = Runtime.getRuntime().maxMemory(); bitmapCache = new BitmapCache((int)Math.min(maxHeapSize / 16, Integer.MAX_VALUE), fileCache); pagesCache = new PagesCache((int)Math.min(maxHeapSize / 6, Integer.MAX_VALUE), serializer); draftsCache = new DraftsCache(10, serializer); database = new Database(this); subscriptions = new Subscriptions(this); downloadingLocker = new DownloadingLocker(); registerChanModules(); Wifi.register(this); } private String getProcessName() { int myPid = android.os.Process.myPid(); for (RunningAppProcessInfo process : ((ActivityManager) getSystemService(ACTIVITY_SERVICE)).getRunningAppProcesses()) { if (myPid == process.pid) return process.processName; } return null; } private boolean isGalleryProcess() { try { return getProcessName().endsWith(":Gallery"); } catch (Exception e) { return false; } } @Override public void onCreate() { super.onCreate(); if (ACRAConstants.ACRA_ENABLED) ACRA.init(this); if (isGalleryProcess()) return; initObjects(); instance = this; } @Override public void onLowMemory() { clearCaches(); super.onLowMemory(); } /** * Очистить все кэши в памяти. Вызывать в случае гроб-гроб-кладбище-OutOfMemory, иногда может помочь */ public static void freeMemory() { try { getInstance().freeMemoryInternal(); } catch (Exception e) {} //если синглтон MainApplication не создан } private void freeMemoryInternal() { clearCaches(); System.gc(); try { Thread.sleep(1000); } catch (Throwable t) {} } private void clearCaches() { pagesCache.clearLru(); bitmapCache.clearLru(); draftsCache.clearLru(); } }