Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incognito for chromium browsers #301

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ForceURL/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
80 changes: 80 additions & 0 deletions ForceURL/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
plugins {
id 'com.android.library'
}

android {
namespace 'com.trianguloy.forceurl'

defaultConfig {
compileSdk 33
minSdkVersion 14
targetSdkVersion 33
versionCode 1
versionName "0.1"
}

buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
alpha {
initWith(buildTypes.debug)
versionNameSuffix '-ALPHA'
}
evaluation {
initWith(buildTypes.debug)
versionNameSuffix '-TEST'
}
}

lint {
// users are free to update translations whenever
// this mean that they are not usually up to date
// and that there are usually lots of missing translations
// so we ignore missing strings
disable 'MissingTranslation'
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}

buildFeatures {
buildConfig true
}
}

// Generate translation related fields
android {
defaultConfig {
// get translations names
def folder = file("src/main/res") // from this folder
def translations = Arrays.stream(folder.list()) // from all files in the folder
.filter { file("$folder.path/$it/strings.xml").exists() } // and containing a strings translation
.collect { it }

// generate buildconfig field with all locales
def locales = translations.stream()
.map { it.replace('values-', '').replace('-r', '-').replace('values', 'en') } // extract the locale code
.distinct().sorted() // keep distinct and sort them
.collect { '"' + it + '"' } // convert as 'string'
.join(',') // and concatenate with commas
println "Found locales: $locales"
buildConfigField "java.util.List<String>", 'LOCALES', "java.util.List.of($locales)" // create field

// generate string resource with all translators
def translators = translations.stream() // for all translations
.map { file("$folder.path/$it/strings.xml") } // get the xml file
.map { new XmlParser().parse(it).find { it.@name == "translators" }?.text() ?: "" } // extract translators
.filter { it != "" } // filter empty
.flatMap { it.split(';').stream() } // split multiple
.map { it.strip() } // trim
.filter { it != "TrianguloY" } // remove myself
.distinct().sorted() // keep distinct and sort them
.collect { it }.join(', ') // join with commas
println "Extracted translators: $translators"
resValue "string", "all_translators", translators // create resource
}
}
21 changes: 21 additions & 0 deletions ForceURL/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
20 changes: 20 additions & 0 deletions ForceURL/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

<application>
<service
android:name="com.trianguloy.forceurl.services.UrlHelperService"
android:exported="true"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>

<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_service_config" />
</service>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.trianguloy.forceurl.data;

import android.view.accessibility.AccessibilityNodeInfo;

/**
* Meant to be used with {@link Apps}. Has one function, which will be called by the
* accessibility helper to help open the URL as needed.
*
* When an implementation of {@link Apps} also implements this, it means it needs help,
* so {@link Apps#needsHelp(Apps)}
*/
public interface AccessibilityFunction {
/**
* Method that will help the UrlHelperService put the URL in the search bar or similar,
* only needed when an url needs help. Will be applied multiple times until the service
* is closed.
*
* @param rootNode
* @param url
* @param pckg
* @return True the moment this method is not needed anymore and the service can be closed.
*/
boolean putUrl(AccessibilityNodeInfo rootNode, String url, String pckg);
}
57 changes: 57 additions & 0 deletions ForceURL/src/main/java/com/trianguloy/forceurl/data/Apps.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.trianguloy.forceurl.data;

import android.content.Context;
import android.content.Intent;

import java.util.Set;

/**
* Represents either a package or a package group (apps), which need additional tweaking
* to open the intent in a specific way (i.e. incognito). These might need additional
* help to be opened.
* <p>
* Must follow the hierarchy data.mode.group.Apps
*/
public interface Apps {
/**
* Returns an id to identify this app/fork
*/
static String getId(Apps app) {
var sep = ".";
var clazz = app.getClass();
var pckgParts = clazz.getName().split("\\.");
return pckgParts[pckgParts.length - 3] + sep +
pckgParts[pckgParts.length - 2] + sep +
pckgParts[pckgParts.length - 1] + sep;
// i.e. "incognito.forks.Chromium"
}

static String getMode(Apps app) {
return getId(app).split("\\.")[0];
}

/**
* Checks if the app/fork is the same as the one we want to open
*/
boolean isThis(Context cntx, String pckg);

/**
* Applies the necessary changes so it opens as we need.
*
* @param intent
*/
void transform(Intent intent);

/**
* @return If the app needs help to input the URL. Internally, it checks if it is an instance of
* {@link AccessibilityFunction})
*/
static boolean needsHelp(Apps app){
return app instanceof AccessibilityFunction;
}

/**
* @return A read only set of extras related to opening the app in this mode
*/
Set<String> getExtras();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.trianguloy.forceurl.data.incognito.forks;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.accessibility.AccessibilityNodeInfo;

import com.trianguloy.forceurl.data.AccessibilityFunction;
import com.trianguloy.forceurl.data.Apps;
import com.trianguloy.forceurl.utilities.methods.AndroidUtils;
import com.trianguloy.forceurl.utilities.methods.JavaUtils;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class Chromium implements Apps, AccessibilityFunction {
// https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java;l=346;drc=fb3fab0be2804a2864783c326518f1acb0402968

private final Set<String> possibleExtras = new HashSet<>();

public Chromium() {
possibleExtras.add("com.google.android.apps.chrome.EXTRA_OPEN_NEW_INCOGNITO_TAB");
possibleExtras.add("EXTRA_OPEN_NEW_INCOGNITO_TAB"); // just in case, but this shouldn't be needed
}

@Override
public boolean isThis(Context cntx, String pckg) {
var mainActivity = AndroidUtils.getMainActivity(cntx, pckg);
// all chromium apps have the same main activity
var activity = "com.google.android.apps.chrome.Main";
return mainActivity.equals(activity);
}

@Override
public void transform(Intent intent) {
// extras do not work in chromium
// https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java;drc=e39fffa6900a679961f5992b8f24a084853b811a;l=1036
// https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java;l=988;drc=fb3fab0be2804a2864783c326518f1acb0402968
intent.setComponent(new ComponentName(
AndroidUtils.getPackage(intent),
"org.chromium.chrome.browser.incognito.IncognitoTabLauncher"));

// XXX: I got a case in API 30 where without this flag, the activity wouldn't launch.
// Maybe it was a one time thing, if it ever happens again, this fixed it.
//intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
}

@Override
public Set<String> getExtras() {
return Collections.unmodifiableSet(possibleExtras);
}

@Override
public boolean putUrl(AccessibilityNodeInfo rootNode, String url, String pckg) {
// FIXME: Call requires API level 16
// put URL in search bar, tap it, then tap first result of dropdown
var searchBar = JavaUtils.getSafe(
rootNode.findAccessibilityNodeInfosByViewId(
pckg + ":id/url_bar"),
0);
var incognitoBadge = JavaUtils.getSafe(
rootNode.findAccessibilityNodeInfosByViewId(
pckg + ":id/location_bar_incognito_badge"),
0);
if (incognitoBadge != null && searchBar != null) {
Bundle arguments = new Bundle();
arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, url);
searchBar.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
searchBar.performAction(AccessibilityNodeInfo.ACTION_CLICK);
var enter = JavaUtils.getSafe(
rootNode.findAccessibilityNodeInfosByViewId(
pckg + ":id/line_1"),
0);
if (enter != null) {
enter.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.trianguloy.forceurl.data.incognito.forks;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;

import com.trianguloy.forceurl.data.Apps;
import com.trianguloy.forceurl.utilities.methods.AndroidUtils;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class Fenix implements Apps {
// https://bugzilla.mozilla.org/show_bug.cgi?id=1807531
// https://github.com/search?q=repo%3Amozilla-mobile%2Ffirefox-android+PRIVATE_BROWSING_MODE&type=code

private final Set<String> possibleExtras = new HashSet<>();
private final Set<String> exclude = new HashSet<>();

public Fenix() {
possibleExtras.add("private_browsing_mode");
// exclude tor browser, as it is always in incognito
exclude.add("org.torproject.torbrowser");
}

@Override
public boolean isThis(Context cntx, String pckg) {
// all fenix apps share the same home activity
var activity = "org.mozilla.fenix.HomeActivity";
if (exclude.contains(pckg)) return false;
Set<String> activities = AndroidUtils.getActivities(cntx, pckg);
return activities.contains(activity);
}

@Override
public void transform(Intent intent) {
intent.setComponent(null);
/*intent.setComponent(new ComponentName(
AndroidUtils.getPackage(intent),
"org.mozilla.fenix.IntentReceiverActivity"));*/
intent.putExtra("private_browsing_mode", true);
}

@Override
public Set<String> getExtras() {
return Collections.unmodifiableSet(possibleExtras);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.trianguloy.forceurl.helpers;

import android.content.Context;

import com.trianguloy.forceurl.data.Apps;

public interface AHelper {

void run(Context cntx, String url, String pckg, Apps app);
}
Loading
Loading