implement url opening

use activity lifecycle callbacks to track foreground state
use labdas for the chansettings
multisite
Floens 8 years ago
parent 839ab5d55b
commit 182da4604c
  1. 1
      Clover/app/src/main/AndroidManifest.xml
  2. 40
      Clover/app/src/main/java/org/floens/chan/Chan.java
  3. 84
      Clover/app/src/main/java/org/floens/chan/chan/ChanHelper.java
  4. 35
      Clover/app/src/main/java/org/floens/chan/core/database/LoadableProvider.java
  5. 2
      Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java
  6. 48
      Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java
  7. 2
      Clover/app/src/main/java/org/floens/chan/core/site/Resolvable.java
  8. 4
      Clover/app/src/main/java/org/floens/chan/core/site/Site.java
  9. 3
      Clover/app/src/main/java/org/floens/chan/core/site/SiteBase.java
  10. 2
      Clover/app/src/main/java/org/floens/chan/core/site/SiteManager.java
  11. 72
      Clover/app/src/main/java/org/floens/chan/core/site/SiteResolver.java
  12. 6
      Clover/app/src/main/java/org/floens/chan/core/site/http/HttpCallManager.java
  13. 62
      Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java
  14. 58
      Clover/app/src/main/java/org/floens/chan/core/site/sites/chan8/Chan8.java
  15. 112
      Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java

@ -72,6 +72,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<data android:host="4chan.org" /> <data android:host="4chan.org" />
<data android:host="www.4chan.org" /> <data android:host="www.4chan.org" />
<data android:host="boards.4chan.org" /> <data android:host="boards.4chan.org" />
<data android:host="8ch.net" />
</intent-filter> </intent-filter>
</activity> </activity>

@ -18,9 +18,11 @@
package org.floens.chan; package org.floens.chan;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Application; import android.app.Application;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
import android.os.Bundle;
import android.os.StrictMode; import android.os.StrictMode;
import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.database.DatabaseManager;
@ -39,7 +41,7 @@ import javax.inject.Inject;
import dagger.ObjectGraph; import dagger.ObjectGraph;
import de.greenrobot.event.EventBus; import de.greenrobot.event.EventBus;
public class Chan extends Application implements UserAgentProvider { public class Chan extends Application implements UserAgentProvider, Application.ActivityLifecycleCallbacks {
private static final String TAG = "ChanApplication"; private static final String TAG = "ChanApplication";
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
@ -78,6 +80,8 @@ public class Chan extends Application implements UserAgentProvider {
final long startTime = Time.startTiming(); final long startTime = Time.startTiming();
registerActivityLifecycleCallbacks(this);
AndroidUtils.init(); AndroidUtils.init();
userAgent = createUserAgent(); userAgent = createUserAgent();
@ -118,7 +122,7 @@ public class Chan extends Application implements UserAgentProvider {
return userAgent; return userAgent;
} }
public void activityEnteredForeground() { private void activityEnteredForeground() {
boolean lastForeground = getApplicationInForeground(); boolean lastForeground = getApplicationInForeground();
activityForegroundCounter++; activityForegroundCounter++;
@ -128,7 +132,7 @@ public class Chan extends Application implements UserAgentProvider {
} }
} }
public void activityEnteredBackground() { private void activityEnteredBackground() {
boolean lastForeground = getApplicationInForeground(); boolean lastForeground = getApplicationInForeground();
activityForegroundCounter--; activityForegroundCounter--;
@ -164,4 +168,34 @@ public class Chan extends Application implements UserAgentProvider {
version = version.toLowerCase(Locale.ENGLISH).replace(" ", "_"); version = version.toLowerCase(Locale.ENGLISH).replace(" ", "_");
return getString(R.string.app_name) + "/" + version; return getString(R.string.app_name) + "/" + version;
} }
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
activityEnteredForeground();
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
activityEnteredBackground();
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
} }

@ -1,84 +0,0 @@
/*
* Clover - 4chan browser https://github.com/Floens/Clover/
* Copyright (C) 2014 Floens
*
* 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 org.floens.chan.chan;
import android.net.Uri;
import org.floens.chan.core.database.DatabaseLoadableManager;
import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.model.orm.Loadable;
import org.floens.chan.core.site.Site;
import org.floens.chan.core.site.Sites;
import java.util.List;
import static org.floens.chan.Chan.getGraph;
public class ChanHelper {
public static Loadable getLoadableFromStartUri(Uri uri) {
Loadable loadable = null;
List<String> parts = uri.getPathSegments();
// TODO(multi-site) get correct site
Site site = Sites.defaultSite();
if (parts.size() > 0) {
String rawBoard = parts.get(0);
DatabaseManager databaseManager = getGraph().get(DatabaseManager.class);
DatabaseLoadableManager loadableManager = databaseManager.getDatabaseLoadableManager();
Board board = site.board(rawBoard);
if (board != null) {
if (parts.size() == 1 || (parts.size() == 2 && "catalog".equals(parts.get(1)))) {
// Board mode
loadable = loadableManager.get(Loadable.forCatalog(board));
} else if (parts.size() >= 3) {
// Thread mode
int no = -1;
try {
no = Integer.parseInt(parts.get(2));
} catch (NumberFormatException ignored) {
}
int post = -1;
String fragment = uri.getFragment();
if (fragment != null) {
int index = fragment.indexOf("p");
if (index >= 0) {
try {
post = Integer.parseInt(fragment.substring(index + 1));
} catch (NumberFormatException ignored) {
}
}
}
if (no >= 0) {
loadable = loadableManager.get(Loadable.forThread(site, board, no));
if (post >= 0) {
loadable.markedNo = post;
}
}
}
}
}
return loadable;
}
}

@ -0,0 +1,35 @@
/*
* Clover - 4chan browser https://github.com/Floens/Clover/
* Copyright (C) 2014 Floens
*
* 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 org.floens.chan.core.database;
import org.floens.chan.core.model.orm.Loadable;
import javax.inject.Inject;
public class LoadableProvider {
private DatabaseManager databaseManager;
@Inject
public LoadableProvider(DatabaseManager databaseManager) {
this.databaseManager = databaseManager;
}
public Loadable get(Loadable definition) {
return databaseManager.getDatabaseLoadableManager().get(definition);
}
}

@ -9,6 +9,7 @@ import org.floens.chan.ChanApplication;
import org.floens.chan.chan.ChanLoader; import org.floens.chan.chan.ChanLoader;
import org.floens.chan.core.cache.FileCache; import org.floens.chan.core.cache.FileCache;
import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.database.LoadableProvider;
import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.manager.BoardManager;
import org.floens.chan.core.manager.FilterEngine; import org.floens.chan.core.manager.FilterEngine;
import org.floens.chan.core.manager.ReplyManager; import org.floens.chan.core.manager.ReplyManager;
@ -61,6 +62,7 @@ import dagger.Provides;
ImageLoader.class, ImageLoader.class,
FileCache.class, FileCache.class,
HttpCallManager.class, HttpCallManager.class,
LoadableProvider.class,
ChanApplication.class, ChanApplication.class,
MainSettingsController.class, MainSettingsController.class,

@ -188,12 +188,8 @@ public class ChanSettings {
developer = new BooleanSetting(p, "preference_developer", false); developer = new BooleanSetting(p, "preference_developer", false);
saveLocation = new StringSetting(p, "preference_image_save_location", Environment.getExternalStorageDirectory() + File.separator + "Clover"); saveLocation = new StringSetting(p, "preference_image_save_location", Environment.getExternalStorageDirectory() + File.separator + "Clover");
saveLocation.addCallback(new Setting.SettingCallback<String>() { saveLocation.addCallback((setting, value) ->
@Override EventBus.getDefault().post(new SettingChanged<>(saveLocation)));
public void onValueChange(Setting setting, String value) {
EventBus.getDefault().post(new SettingChanged<>(saveLocation));
}
});
saveOriginalFilename = new BooleanSetting(p, "preference_image_save_original", false); saveOriginalFilename = new BooleanSetting(p, "preference_image_save_original", false);
shareUrl = new BooleanSetting(p, "preference_image_share_url", false); shareUrl = new BooleanSetting(p, "preference_image_share_url", false);
networkHttps = new BooleanSetting(p, "preference_network_https", true); networkHttps = new BooleanSetting(p, "preference_network_https", true);
@ -209,26 +205,18 @@ public class ChanSettings {
volumeKeysScrolling = new BooleanSetting(p, "preference_volume_key_scrolling", false); volumeKeysScrolling = new BooleanSetting(p, "preference_volume_key_scrolling", false);
postFullDate = new BooleanSetting(p, "preference_post_full_date", false); postFullDate = new BooleanSetting(p, "preference_post_full_date", false);
postFileInfo = new BooleanSetting(p, "preference_post_file_info", true); postFileInfo = new BooleanSetting(p, "preference_post_file_info", true);
postFilename = new BooleanSetting(p, "preference_post_filename", false); postFilename = new BooleanSetting(p, "preference_post_filename", true);
neverHideToolbar = new BooleanSetting(p, "preference_never_hide_toolbar", false); neverHideToolbar = new BooleanSetting(p, "preference_never_hide_toolbar", false);
controllerSwipeable = new BooleanSetting(p, "preference_controller_swipeable", true); controllerSwipeable = new BooleanSetting(p, "preference_controller_swipeable", true);
saveBoardFolder = new BooleanSetting(p, "preference_save_subboard", false); saveBoardFolder = new BooleanSetting(p, "preference_save_subboard", false);
watchEnabled = new BooleanSetting(p, "preference_watch_enabled", false); watchEnabled = new BooleanSetting(p, "preference_watch_enabled", false);
watchEnabled.addCallback(new Setting.SettingCallback<Boolean>() { watchEnabled.addCallback((setting, value) ->
@Override EventBus.getDefault().post(new SettingChanged<>(watchEnabled)));
public void onValueChange(Setting setting, Boolean value) {
EventBus.getDefault().post(new SettingChanged<>(watchEnabled));
}
});
watchCountdown = new BooleanSetting(p, "preference_watch_countdown", false); watchCountdown = new BooleanSetting(p, "preference_watch_countdown", false);
watchBackground = new BooleanSetting(p, "preference_watch_background_enabled", false); watchBackground = new BooleanSetting(p, "preference_watch_background_enabled", false);
watchBackground.addCallback(new Setting.SettingCallback<Boolean>() { watchBackground.addCallback((setting, value) ->
@Override EventBus.getDefault().post(new SettingChanged<>(watchBackground)));
public void onValueChange(Setting setting, Boolean value) {
EventBus.getDefault().post(new SettingChanged<>(watchBackground));
}
});
watchBackgroundInterval = new IntegerSetting(p, "preference_watch_background_interval", WatchManager.DEFAULT_BACKGROUND_INTERVAL); watchBackgroundInterval = new IntegerSetting(p, "preference_watch_background_interval", WatchManager.DEFAULT_BACKGROUND_INTERVAL);
watchNotifyMode = new StringSetting(p, "preference_watch_notify_mode", "all"); watchNotifyMode = new StringSetting(p, "preference_watch_notify_mode", "all");
watchSound = new StringSetting(p, "preference_watch_sound", "quotes"); watchSound = new StringSetting(p, "preference_watch_sound", "quotes");
@ -240,26 +228,12 @@ public class ChanSettings {
previousVersion = new IntegerSetting(p, "preference_previous_version", 0); previousVersion = new IntegerSetting(p, "preference_previous_version", 0);
proxyEnabled = new BooleanSetting(p, "preference_proxy_enabled", false); proxyEnabled = new BooleanSetting(p, "preference_proxy_enabled", false);
proxyEnabled.addCallback(new Setting.SettingCallback<Boolean>() {
@Override
public void onValueChange(Setting setting, Boolean value) {
loadProxy();
}
});
proxyAddress = new StringSetting(p, "preference_proxy_address", ""); proxyAddress = new StringSetting(p, "preference_proxy_address", "");
proxyAddress.addCallback(new Setting.SettingCallback<String>() {
@Override
public void onValueChange(Setting setting, String value) {
loadProxy();
}
});
proxyPort = new IntegerSetting(p, "preference_proxy_port", 80); proxyPort = new IntegerSetting(p, "preference_proxy_port", 80);
proxyPort.addCallback(new Setting.SettingCallback<Integer>() {
@Override proxyEnabled.addCallback((setting, value) -> loadProxy());
public void onValueChange(Setting setting, Integer value) { proxyAddress.addCallback((setting, value) -> loadProxy());
loadProxy(); proxyPort.addCallback((setting, value) -> loadProxy());
}
});
loadProxy(); loadProxy();
settingsOpenCounter = new CounterSetting(p, "counter_settings_open"); settingsOpenCounter = new CounterSetting(p, "counter_settings_open");

@ -8,7 +8,7 @@ public interface Resolvable {
FULL_MATCH FULL_MATCH
} }
ResolveResult resolve(String value); ResolveResult matchesName(String value);
Class<? extends Site> getSiteClass(); Class<? extends Site> getSiteClass();
} }

@ -33,6 +33,8 @@ import org.floens.chan.core.site.http.LoginResponse;
import org.floens.chan.core.site.http.Reply; import org.floens.chan.core.site.http.Reply;
import org.floens.chan.core.site.http.ReplyResponse; import org.floens.chan.core.site.http.ReplyResponse;
import okhttp3.HttpUrl;
public interface Site { public interface Site {
enum Feature { enum Feature {
/** /**
@ -129,6 +131,8 @@ public interface Site {
SiteIcon icon(); SiteIcon icon();
Loadable respondsTo(HttpUrl url);
boolean feature(Feature feature); boolean feature(Feature feature);
boolean boardFeature(BoardFeature boardFeature, Board board); boolean boardFeature(BoardFeature boardFeature, Board board);

@ -20,6 +20,7 @@ package org.floens.chan.core.site;
import com.android.volley.RequestQueue; import com.android.volley.RequestQueue;
import org.floens.chan.core.database.LoadableProvider;
import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.manager.BoardManager;
import org.floens.chan.core.model.json.site.SiteConfig; import org.floens.chan.core.model.json.site.SiteConfig;
import org.floens.chan.core.model.json.site.SiteUserSettings; import org.floens.chan.core.model.json.site.SiteUserSettings;
@ -40,6 +41,7 @@ public abstract class SiteBase implements Site {
protected HttpCallManager httpCallManager; protected HttpCallManager httpCallManager;
protected RequestQueue requestQueue; protected RequestQueue requestQueue;
protected BoardManager boardManager; protected BoardManager boardManager;
protected LoadableProvider loadableProvider;
@Override @Override
public void initialize(int id, SiteConfig config, SiteUserSettings userSettings) { public void initialize(int id, SiteConfig config, SiteUserSettings userSettings) {
@ -55,6 +57,7 @@ public abstract class SiteBase implements Site {
httpCallManager = graph.get(HttpCallManager.class); httpCallManager = graph.get(HttpCallManager.class);
requestQueue = graph.get(RequestQueue.class); requestQueue = graph.get(RequestQueue.class);
boardManager = graph.get(BoardManager.class); boardManager = graph.get(BoardManager.class);
loadableProvider = graph.get(LoadableProvider.class);
if (boardsType() == BoardsType.DYNAMIC) { if (boardsType() == BoardsType.DYNAMIC) {
boards(boards -> boardManager.createAll(boards.boards)); boards(boards -> boardManager.createAll(boards.boards));

@ -47,7 +47,7 @@ public class SiteManager {
} }
public void addSite(String url, SiteAddCallback callback) { public void addSite(String url, SiteAddCallback callback) {
SiteResolver.SiteResolverResult resolve = resolver.resolve(url); SiteResolver.SiteResolverResult resolve = resolver.resolveSiteForUrl(url);
Class<? extends Site> siteClass; Class<? extends Site> siteClass;
if (resolve.match == SiteResolver.SiteResolverResult.Match.BUILTIN) { if (resolve.match == SiteResolver.SiteResolverResult.Match.BUILTIN) {

@ -18,35 +18,33 @@
package org.floens.chan.core.site; package org.floens.chan.core.site;
import android.support.annotation.Nullable;
import org.floens.chan.core.database.LoadableProvider;
import org.floens.chan.core.model.orm.Loadable;
import java.util.List; import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
class SiteResolver { public class SiteResolver {
private LoadableProvider loadableProvider;
@Inject @Inject
public SiteResolver() { public SiteResolver(LoadableProvider loadableProvider) {
this.loadableProvider = loadableProvider;
} }
SiteResolverResult resolve(String url) { SiteResolverResult resolveSiteForUrl(String url) {
List<Resolvable> resolvables = Sites.RESOLVABLES; List<Resolvable> resolvables = Sites.RESOLVABLES;
HttpUrl httpUrl = HttpUrl.parse(url); HttpUrl httpUrl = sanitizeUrl(url);
if (httpUrl == null) {
httpUrl = HttpUrl.parse("https://" + url);
}
if (httpUrl != null) {
if (httpUrl.host().indexOf('.') < 0) {
httpUrl = null;
}
}
if (httpUrl == null) { if (httpUrl == null) {
for (Resolvable resolvable : resolvables) { for (Resolvable resolvable : resolvables) {
if (resolvable.resolve(url) == Resolvable.ResolveResult.NAME_MATCH) { if (resolvable.matchesName(url) == Resolvable.ResolveResult.NAME_MATCH) {
return new SiteResolverResult(SiteResolverResult.Match.BUILTIN, resolvable.getSiteClass(), null); return new SiteResolverResult(SiteResolverResult.Match.BUILTIN, resolvable.getSiteClass(), null);
} }
} }
@ -59,7 +57,7 @@ class SiteResolver {
} }
for (Resolvable resolvable : resolvables) { for (Resolvable resolvable : resolvables) {
if (resolvable.resolve(httpUrl.toString()) == Resolvable.ResolveResult.FULL_MATCH) { if (resolvable.matchesName(httpUrl.toString()) == Resolvable.ResolveResult.FULL_MATCH) {
return new SiteResolverResult(SiteResolverResult.Match.BUILTIN, resolvable.getSiteClass(), null); return new SiteResolverResult(SiteResolverResult.Match.BUILTIN, resolvable.getSiteClass(), null);
} }
} }
@ -67,6 +65,40 @@ class SiteResolver {
return new SiteResolverResult(SiteResolverResult.Match.EXTERNAL, null, httpUrl); return new SiteResolverResult(SiteResolverResult.Match.EXTERNAL, null, httpUrl);
} }
public LoadableResult resolveLoadableForUrl(String url) {
final HttpUrl httpUrl = sanitizeUrl(url);
if (httpUrl == null) {
return null;
}
for (Site site : Sites.allSites()) {
Loadable resolved = site.respondsTo(httpUrl);
if (resolved != null) {
return new LoadableResult(resolved);
}
}
return null;
}
@Nullable
private HttpUrl sanitizeUrl(String url) {
HttpUrl httpUrl = HttpUrl.parse(url);
if (httpUrl == null) {
httpUrl = HttpUrl.parse("https://" + url);
}
if (httpUrl != null) {
if (httpUrl.host().indexOf('.') < 0) {
httpUrl = null;
}
}
return httpUrl;
}
static class SiteResolverResult { static class SiteResolverResult {
enum Match { enum Match {
NONE, NONE,
@ -84,4 +116,12 @@ class SiteResolver {
this.externalResult = externalResult; this.externalResult = externalResult;
} }
} }
public static class LoadableResult {
public final Loadable loadable;
public LoadableResult(Loadable loadable) {
this.loadable = loadable;
}
}
} }

@ -20,6 +20,7 @@ package org.floens.chan.core.site.http;
import org.floens.chan.core.di.UserAgentProvider; import org.floens.chan.core.di.UserAgentProvider;
import org.floens.chan.core.site.Site; import org.floens.chan.core.site.Site;
import org.floens.chan.core.site.SiteRequestModifier;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -58,7 +59,10 @@ public class HttpCallManager {
httpCall.setup(requestBuilder); httpCall.setup(requestBuilder);
if (site != null) { if (site != null) {
site.requestModifier().modifyHttpCall(httpCall, requestBuilder); final SiteRequestModifier siteRequestModifier = site.requestModifier();
if (siteRequestModifier != null) {
siteRequestModifier.modifyHttpCall(httpCall, requestBuilder);
}
} }
requestBuilder.header("User-Agent", userAgentProvider.getUserAgent()); requestBuilder.header("User-Agent", userAgentProvider.getUserAgent());

@ -59,12 +59,13 @@ import okhttp3.Request;
public class Chan4 extends SiteBase { public class Chan4 extends SiteBase {
public static final Resolvable RESOLVABLE = new Resolvable() { public static final Resolvable RESOLVABLE = new Resolvable() {
@Override @Override
public ResolveResult resolve(String value) { public ResolveResult matchesName(String value) {
if (value.equals("4chan")) { switch (value) {
case "4chan":
return ResolveResult.NAME_MATCH; return ResolveResult.NAME_MATCH;
} else if (value.equals("https://4chan.org/")) { case "https://4chan.org/":
return ResolveResult.FULL_MATCH; return ResolveResult.FULL_MATCH;
} else { default:
return ResolveResult.NO; return ResolveResult.NO;
} }
} }
@ -270,6 +271,59 @@ public class Chan4 extends SiteBase {
return SiteIcon.fromAssets("icons/4chan.png"); return SiteIcon.fromAssets("icons/4chan.png");
} }
@Override
public Loadable respondsTo(HttpUrl url) {
boolean responds = url.host().equals("4chan.org") ||
url.host().equals("www.4chan.org") ||
url.host().equals("boards.4chan.org");
if (responds) {
List<String> parts = url.pathSegments();
if (!parts.isEmpty()) {
String boardCode = parts.get(0);
Board board = board(boardCode);
if (board != null) {
if (parts.size() < 3) {
// Board mode
return loadableProvider.get(Loadable.forCatalog(board));
} else if (parts.size() >= 3) {
// Thread mode
int no = -1;
try {
no = Integer.parseInt(parts.get(2));
} catch (NumberFormatException ignored) {
}
int post = -1;
String fragment = url.fragment();
if (fragment != null) {
int index = fragment.indexOf("p");
if (index >= 0) {
try {
post = Integer.parseInt(fragment.substring(index + 1));
} catch (NumberFormatException ignored) {
}
}
}
if (no >= 0) {
Loadable loadable = loadableProvider.get(
Loadable.forThread(this, board, no));
if (post >= 0) {
loadable.markedNo = post;
}
return loadable;
}
}
}
}
}
return null;
}
@Override @Override
public boolean feature(Feature feature) { public boolean feature(Feature feature) {
switch (feature) { switch (feature) {

@ -42,6 +42,7 @@ import org.floens.chan.core.site.http.LoginRequest;
import org.floens.chan.core.site.http.Reply; import org.floens.chan.core.site.http.Reply;
import org.floens.chan.utils.Logger; import org.floens.chan.utils.Logger;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -55,12 +56,13 @@ public class Chan8 extends SiteBase {
public static final Resolvable RESOLVABLE = new Resolvable() { public static final Resolvable RESOLVABLE = new Resolvable() {
@Override @Override
public ResolveResult resolve(String value) { public ResolveResult matchesName(String value) {
if (value.equals("8chan")) { switch (value) {
case "8chan":
return ResolveResult.NAME_MATCH; return ResolveResult.NAME_MATCH;
} else if (value.equals("https://8ch.net/")) { case "https://8ch.net/":
return ResolveResult.FULL_MATCH; return ResolveResult.FULL_MATCH;
} else { default:
return ResolveResult.NO; return ResolveResult.NO;
} }
} }
@ -184,6 +186,54 @@ public class Chan8 extends SiteBase {
return SiteIcon.fromAssets("icons/8chan.png"); return SiteIcon.fromAssets("icons/8chan.png");
} }
@Override
public Loadable respondsTo(HttpUrl url) {
boolean responds = url.host().equals("8ch.net");
if (responds) {
List<String> parts = url.pathSegments();
if (!parts.isEmpty()) {
String boardCode = parts.get(0);
Board board = board(boardCode);
if (board != null) {
if (parts.size() < 3) {
// Board mode
return loadableProvider.get(Loadable.forCatalog(board));
} else if (parts.size() >= 3) {
// Thread mode
int no = -1;
try {
no = Integer.parseInt(parts.get(2).replace(".html", ""));
} catch (NumberFormatException ignored) {
}
int post = -1;
String fragment = url.fragment();
if (fragment != null) {
try {
post = Integer.parseInt(fragment);
} catch (NumberFormatException ignored) {
}
}
if (no >= 0) {
Loadable loadable = loadableProvider.get(
Loadable.forThread(this, board, no));
if (post >= 0) {
loadable.markedNo = post;
}
return loadable;
}
}
}
}
}
return null;
}
@Override @Override
public boolean feature(Feature feature) { public boolean feature(Feature feature) {
switch (feature) { switch (feature) {

@ -20,6 +20,7 @@ package org.floens.chan.ui.activity;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.net.Uri;
import android.nfc.NdefMessage; import android.nfc.NdefMessage;
import android.nfc.NfcAdapter; import android.nfc.NfcAdapter;
import android.nfc.NfcEvent; import android.nfc.NfcEvent;
@ -32,7 +33,6 @@ import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.floens.chan.Chan;
import org.floens.chan.R; import org.floens.chan.R;
import org.floens.chan.controller.Controller; import org.floens.chan.controller.Controller;
import org.floens.chan.controller.NavigationController; import org.floens.chan.controller.NavigationController;
@ -45,6 +45,7 @@ import org.floens.chan.core.model.orm.Loadable;
import org.floens.chan.core.model.orm.Pin; import org.floens.chan.core.model.orm.Pin;
import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.settings.ChanSettings;
import org.floens.chan.core.site.Site; import org.floens.chan.core.site.Site;
import org.floens.chan.core.site.SiteResolver;
import org.floens.chan.core.site.Sites; import org.floens.chan.core.site.Sites;
import org.floens.chan.ui.controller.BrowseController; import org.floens.chan.ui.controller.BrowseController;
import org.floens.chan.ui.controller.DoubleNavigationController; import org.floens.chan.ui.controller.DoubleNavigationController;
@ -94,6 +95,9 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat
@Inject @Inject
WatchManager watchManager; WatchManager watchManager;
@Inject
SiteResolver siteResolver;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -132,62 +136,78 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat
} }
private void setupFromStateOrFreshLaunch(Bundle savedInstanceState) { private void setupFromStateOrFreshLaunch(Bundle savedInstanceState) {
boolean loadDefault = true; boolean handled;
/*if (savedInstanceState != null) { if (savedInstanceState != null) {
// Restore the activity state from the previously saved state. handled = restoreFromSavedState(savedInstanceState);
ChanState chanState = savedInstanceState.getParcelable(STATE_KEY);
if (chanState == null) {
Logger.w(TAG, "savedInstanceState was not null, but no ChanState was found!");
} else { } else {
Pair<Loadable, Loadable> boardThreadPair = resolveChanState(chanState); handled = restoreFromUrl();
if (boardThreadPair != null && boardThreadPair.first != null) {
loadDefault = false;
browseController.setBoard(boardThreadPair.first.board);
if (boardThreadPair.second != null) {
browseController.showThread(boardThreadPair.second);
} }
// Not from a state or from an url, launch the setup controller if no boards are setup up yet,
// otherwise load the default saved board.
if (!handled) {
/*if (boardManager.getSavedBoards().isEmpty()) {
setupWithNoBoards();
} else {
browseController.loadWithDefaultBoard();
}*/
browseController.loadWithDefaultBoard();
} }
} }
} else {
private boolean restoreFromUrl() {
boolean handled = false;
final Uri data = getIntent().getData(); final Uri data = getIntent().getData();
// Start from an url launch. // Start from an url launch.
if (data != null) { if (data != null) {
Loadable fromUri = ChanHelper.getLoadableFromStartUri(data); final SiteResolver.LoadableResult loadableResult =
if (fromUri != null) { siteResolver.resolveLoadableForUrl(data.toString());
loadDefault = false;
browseController.setBoard(fromUri.board);
if (fromUri.isThreadMode()) { if (loadableResult != null) {
browseController.showThread(fromUri, false); handled = true;
Loadable loadable = loadableResult.loadable;
browseController.setBoard(loadable.board);
if (loadable.isThreadMode()) {
browseController.showThread(loadable, false);
} }
} else { } else {
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setMessage(R.string.open_link_not_matched) .setMessage(R.string.open_link_not_matched)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { .setPositiveButton(R.string.ok, (dialog, which) ->
@Override AndroidUtils.openLink(data.toString()))
public void onClick(DialogInterface dialog, int which) {
AndroidUtils.openLink(data.toString());
}
})
.show(); .show();
} }
} }
}*/
// Not from a state or from an url, launch the setup controller if no boards are setup up yet, return handled;
// otherwise load the default saved board. }
if (loadDefault) {
/*if (boardManager.getSavedBoards().isEmpty()) { private boolean restoreFromSavedState(Bundle savedInstanceState) {
setupWithNoBoards(); boolean handled = true;
// Restore the activity state from the previously saved state.
ChanState chanState = savedInstanceState.getParcelable(STATE_KEY);
if (chanState == null) {
Logger.w(TAG, "savedInstanceState was not null, but no ChanState was found!");
} else { } else {
browseController.loadWithDefaultBoard(); Pair<Loadable, Loadable> boardThreadPair = resolveChanState(chanState);
}*/
browseController.loadWithDefaultBoard(); if (boardThreadPair != null && boardThreadPair.first != null) {
handled = true;
browseController.setBoard(boardThreadPair.first.board);
if (boardThreadPair.second != null) {
browseController.showThread(boardThreadPair.second);
} }
} }
}
return handled;
}
private void setupWithNoBoards() { private void setupWithNoBoards() {
mainNavigationController.presentController(new SetupController(this), false); mainNavigationController.presentController(new SetupController(this), false);
@ -197,6 +217,7 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat
DatabaseLoadableManager loadableManager = databaseManager.getDatabaseLoadableManager(); DatabaseLoadableManager loadableManager = databaseManager.getDatabaseLoadableManager();
Site site = Sites.forId(state.board.siteId); Site site = Sites.forId(state.board.siteId);
if (site != null) {
Board board = site.board(state.board.boardCode); Board board = site.board(state.board.boardCode);
if (board != null) { if (board != null) {
state.board.site = site; state.board.site = site;
@ -209,6 +230,7 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat
return new Pair<>(boardLoadable, threadLoadable.mode == Loadable.Mode.THREAD ? threadLoadable : null); return new Pair<>(boardLoadable, threadLoadable.mode == Loadable.Mode.THREAD ? threadLoadable : null);
} }
}
return null; return null;
} }
@ -431,20 +453,6 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat
stack.clear(); stack.clear();
} }
@Override
protected void onStart() {
super.onStart();
Chan.getInstance().activityEnteredForeground();
}
@Override
protected void onStop() {
super.onStop();
Chan.getInstance().activityEnteredBackground();
}
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);

Loading…
Cancel
Save