0
0
mirror of https://github.com/TrianguloY/UrlChecker.git synced 2024-09-20 04:12:14 +02:00

Merge pull request #164 from PabloOQ/tutorial

Tutorial screen
This commit is contained in:
TrianguloY 2023-02-03 23:46:12 +01:00 committed by GitHub
commit bc41162d62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 870 additions and 146 deletions

View File

@ -24,6 +24,7 @@
<activity android:name=".activities.AboutActivity" />
<activity android:name=".activities.ModulesActivity" />
<activity android:name=".activities.SettingsActivity" />
<activity android:name=".activities.TutorialActivity" />
<activity
android:name=".dialogs.MainDialog"
android:excludeFromRecents="true"

View File

@ -27,6 +27,11 @@ public class MainActivity extends Activity {
AndroidSettings.setTheme(this, false);
AndroidSettings.setLocale(this);
setContentView(R.layout.activity_main);
// open tutorial if not done yet
if (!TutorialActivity.DONE(this).get()) {
PackageUtils.startActivity(new Intent(this, TutorialActivity.class), R.string.toast_noApp, this);
}
}
@Override

View File

@ -1,21 +1,17 @@
package com.trianguloy.urlchecker.activities;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.role.RoleManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.Toast;
import com.trianguloy.urlchecker.R;
import com.trianguloy.urlchecker.fragments.ActivityResultInjector;
import com.trianguloy.urlchecker.fragments.BrowserButtonsFragment;
import com.trianguloy.urlchecker.utilities.AndroidSettings;
import com.trianguloy.urlchecker.utilities.AndroidUtils;
import com.trianguloy.urlchecker.utilities.PackageUtils;
@ -27,9 +23,6 @@ import java.util.Objects;
*/
public class SettingsActivity extends Activity {
public static final int REQUEST_CODE = 2;
private RoleManager roleManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -55,94 +48,19 @@ public class SettingsActivity extends Activity {
return super.onOptionsItemSelected(item);
}
/* ------------------- default browser ------------------- */
/* ------------------- configure browser ------------------- */
// adapted from https://stackoverflow.com/a/74108806
private final ActivityResultInjector activityResultInjector = new ActivityResultInjector();
private final BrowserButtonsFragment browserButtons = new BrowserButtonsFragment(this, activityResultInjector);
/**
* hide buttons if not available
*/
private void configureBrowserButtons() {
boolean hide = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
roleManager = getSystemService(RoleManager.class);
if (roleManager.isRoleAvailable(RoleManager.ROLE_BROWSER)) {
hide = false;
}
}
if (hide) findViewById(R.id.b1).setVisibility(View.GONE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
findViewById(R.id.b2).setVisibility(View.GONE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S)
findViewById(R.id.b3).setVisibility(View.GONE);
}
/**
* open a specific dialog to choose the browser
*/
@TargetApi(Build.VERSION_CODES.Q)
public void chooseBrowserPopup(View view) {
PackageUtils.startActivityForResult(roleManager.createRequestRoleIntent(RoleManager.ROLE_BROWSER), REQUEST_CODE, R.string.toast_noApp, this);
browserButtons.onInitialize(findViewById(browserButtons.getLayoutId()));
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != REQUEST_CODE) {
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (!activityResultInjector.onActivityResult(requestCode, resultCode, data))
super.onActivityResult(requestCode, resultCode, data);
return;
}
if (resultCode == Activity.RESULT_OK) {
Toast.makeText(this, R.string.toast_defaultSet, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, R.string.canceled, Toast.LENGTH_LONG).show();
}
}
/**
* Open android settings about the default browser
*/
@TargetApi(Build.VERSION_CODES.N)
public void openBrowserSettings(View view) {
// open the settings
Intent intent = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
intent.putExtra(
":settings:fragment_args_key",
"default_browser"
);
Bundle bundle = new Bundle();
bundle.putString(":settings:fragment_args_key", "default_browser");
intent.putExtra(
":settings:show_fragment_args",
bundle
);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PackageUtils.startActivity(intent, R.string.toast_noApp, this);
}
// adapted from https://groups.google.com/g/androidscript/c/cLq7eiUVpig/m/RDraxFYQCgAJ
/**
* Open the android app settings to open links as default
*/
@TargetApi(Build.VERSION_CODES.S)
public void openAppLinks(View view) {
PackageUtils.startActivity(new Intent(
Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
Uri.parse("package:" + getPackageName())
), R.string.toast_noApp, this);
}
/**
* Open the android app settings
*/
public void openAppDetails(View view) {
PackageUtils.startActivity(new Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.parse("package:" + getPackageName())
), R.string.toast_noApp, this);
}
/* ------------------- day/night ------------------- */
@ -198,4 +116,10 @@ public class SettingsActivity extends Activity {
}
});
}
/* ------------------- tutorial ------------------- */
public void openTutorial(View view) {
PackageUtils.startActivity(new Intent(this, TutorialActivity.class), R.string.toast_noApp, this);
}
}

View File

@ -0,0 +1,131 @@
package com.trianguloy.urlchecker.activities;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.ViewFlipper;
import com.trianguloy.urlchecker.R;
import com.trianguloy.urlchecker.fragments.ActivityResultInjector;
import com.trianguloy.urlchecker.fragments.BrowserButtonsFragment;
import com.trianguloy.urlchecker.utilities.AndroidSettings;
import com.trianguloy.urlchecker.utilities.DoubleEvent;
import com.trianguloy.urlchecker.utilities.GenericPref;
import com.trianguloy.urlchecker.utilities.PackageUtils;
import java.util.Locale;
public class TutorialActivity extends Activity {
private final DoubleEvent doubleClick = new DoubleEvent(500); // to avoid exiting when pressing next/prev very fast
private Button prevButton;
private Button nextButton;
private GenericPref.Bool tutorialDone;
private ViewFlipper flipper;
private TextView pageIndexText;
public static GenericPref.Bool DONE(Context cntx) {
return new GenericPref.Bool("tutorial_done", false, cntx);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidSettings.setTheme(this, false);
AndroidSettings.setLocale(this);
setContentView(R.layout.activity_tutorial);
setTitle(R.string.tutorial);
tutorialDone = DONE(this);
flipper = findViewById(R.id.flipper);
prevButton = findViewById(R.id.bBack);
nextButton = findViewById(R.id.bNext);
pageIndexText = findViewById(R.id.pageIndex);
configureBrowserButtons();
updateButtons();
}
/* ------------------- browser fragment ------------------- */
private final ActivityResultInjector activityResultInjector = new ActivityResultInjector();
private final BrowserButtonsFragment browserButtons = new BrowserButtonsFragment(this, activityResultInjector);
private void configureBrowserButtons() {
browserButtons.onInitialize(findViewById(browserButtons.getLayoutId()));
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (!activityResultInjector.onActivityResult(requestCode, resultCode, data))
super.onActivityResult(requestCode, resultCode, data);
}
/* ------------------- buttons ------------------- */
@Override
public void onBackPressed() {
prev(null);
}
public void prev(View view) {
if (flipper.getDisplayedChild() == 0) {
// first page, exit
if (view != null && doubleClick.checkAndTrigger()) return; // unless clicked too fast
exit();
} else {
// show prev
flipper.showPrevious();
doubleClick.trigger();
updateButtons();
}
}
public void next(View view) {
if (flipper.getDisplayedChild() == flipper.getChildCount() - 1) {
// last page, exit
if (doubleClick.checkAndTrigger()) return; // unless clicked too fast
exit();
} else {
// show next
flipper.showNext();
doubleClick.trigger();
updateButtons();
}
}
public void openModulesActivity(View view) {
PackageUtils.startActivity(new Intent(this, ModulesActivity.class), R.string.toast_noApp, this);
}
/* ------------------- actions ------------------- */
/**
* Updates the buttons and index texts
*/
private void updateButtons() {
var current = flipper.getDisplayedChild();
var max = flipper.getChildCount();
prevButton.setText(current == 0 ? R.string.btn_tutorialSkip : R.string.back);
nextButton.setText(current != max - 1 ? R.string.next : R.string.btn_tutorialEnd);
pageIndexText.setText(String.format(Locale.getDefault(), "%d/%d", current + 1, max));
}
/**
* Marks the tutorial as completed and exits
*/
private void exit() {
tutorialDone.set(true);
this.finish();
}
}

View File

@ -0,0 +1,64 @@
package com.trianguloy.urlchecker.fragments;
import android.content.Intent;
import android.util.Log;
import com.trianguloy.urlchecker.utilities.AndroidUtils;
import java.util.ArrayList;
import java.util.List;
/**
* An injector to allow registering for activityResult.
* Alternative to using the deprecate android fragments (and without using the huge compatibility library)
*/
public class ActivityResultInjector {
private static final int RESERVED = 123; // just in case, registered listeners will have requestCode >= this
private final List<Listener> listeners = new ArrayList<>();
/* ------------------- client use ------------------- */
interface Listener {
/**
* Called when the event fires for a particular registrar
*/
void onActivityResult(int resultCode, Intent data);
}
/**
* Call this to register an onActivityResult listener. Returns the requestCode you must use in the startActivityForResult call
*/
public int register(Listener listener) {
listeners.add(listener);
return RESERVED + listeners.size() - 1;
}
/* ------------------- activity use ------------------- */
/**
* An activity must use this as:
* <pre>
* @Override
* public void onActivityResult(int requestCode, int resultCode, Intent data) {
* if (!activityResultInjector.onActivityResult(requestCode, resultCode, data))
* super.onActivityResult(requestCode, resultCode, data);
* }
* </pre>
*/
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
var index = requestCode - RESERVED;
if (index < 0) {
// external
Log.d("ACTIVITY_RESULT", "External request code (" + requestCode + "), consider using ActivityResultInjector");
return false;
}
if (index >= listeners.size()) {
// external?
AndroidUtils.assertError("Invalid request code (" + requestCode + "), consider using ActivityResultInjector or a requestCode less than " + RESERVED);
return false;
}
listeners.get(index).onActivityResult(resultCode, data);
return true;
}
}

View File

@ -0,0 +1,132 @@
package com.trianguloy.urlchecker.fragments;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.role.RoleManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.trianguloy.urlchecker.R;
import com.trianguloy.urlchecker.utilities.PackageUtils;
public class BrowserButtonsFragment implements Fragment, ActivityResultInjector.Listener {
private final Activity cntx;
private final int requestCode;
private RoleManager roleManager;
public BrowserButtonsFragment(Activity cntx, ActivityResultInjector activityResultInjector) {
this.cntx = cntx;
requestCode = activityResultInjector.register(this);
}
@Override
public int getLayoutId() {
return R.id.browser_buttons;
}
@Override
public void onInitialize(View views) {
configureBrowserButtons(views);
}
// adapted from https://stackoverflow.com/a/74108806
/**
* hide buttons if not available
*/
private void configureBrowserButtons(View view) {
Button b1 = view.findViewById(R.id.b1);
Button b2 = view.findViewById(R.id.b2);
Button b3 = view.findViewById(R.id.b3);
Button b4 = view.findViewById(R.id.b4);
boolean hide = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
roleManager = cntx.getSystemService(RoleManager.class);
if (roleManager.isRoleAvailable(RoleManager.ROLE_BROWSER)) {
hide = false;
}
}
if (hide) b1.setVisibility(View.GONE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
b2.setVisibility(View.GONE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S)
b3.setVisibility(View.GONE);
b1.setOnClickListener(v -> chooseBrowserPopup());
b2.setOnClickListener(v -> openBrowserSettings());
b3.setOnClickListener(v -> openAppLinks());
b4.setOnClickListener(v -> openAppDetails());
}
/**
* open a specific dialog to choose the browser
*/
@TargetApi(Build.VERSION_CODES.Q)
public void chooseBrowserPopup() {
cntx.startActivityForResult(roleManager.createRequestRoleIntent(RoleManager.ROLE_BROWSER), requestCode);
}
@Override
public void onActivityResult(int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
Toast.makeText(cntx, R.string.toast_defaultSet, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(cntx, R.string.canceled, Toast.LENGTH_LONG).show();
}
}
/**
* Open android settings about the default browser
*/
@TargetApi(Build.VERSION_CODES.N)
public void openBrowserSettings() {
// open the settings
Intent intent = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
intent.putExtra(
":settings:fragment_args_key",
"default_browser"
);
Bundle bundle = new Bundle();
bundle.putString(":settings:fragment_args_key", "default_browser");
intent.putExtra(
":settings:show_fragment_args",
bundle
);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PackageUtils.startActivity(intent, R.string.toast_noApp, cntx);
}
// adapted from https://groups.google.com/g/androidscript/c/cLq7eiUVpig/m/RDraxFYQCgAJ
/**
* Open the android app settings to open links as default
*/
@TargetApi(Build.VERSION_CODES.S)
public void openAppLinks() {
PackageUtils.startActivity(new Intent(
Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
Uri.parse("package:" + cntx.getPackageName())
), R.string.toast_noApp, cntx);
}
/**
* Open the android app settings
*/
public void openAppDetails() {
PackageUtils.startActivity(new Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.parse("package:" + cntx.getPackageName())
), R.string.toast_noApp, cntx);
}
}

View File

@ -1,4 +1,4 @@
package com.trianguloy.urlchecker.utilities;
package com.trianguloy.urlchecker.fragments;
import android.view.View;

View File

@ -3,7 +3,7 @@ package com.trianguloy.urlchecker.modules;
import android.app.Activity;
import com.trianguloy.urlchecker.activities.ModulesActivity;
import com.trianguloy.urlchecker.utilities.Fragment;
import com.trianguloy.urlchecker.fragments.Fragment;
/**
* Base class for a module configuration fragment

View File

@ -3,8 +3,8 @@ package com.trianguloy.urlchecker.modules;
import android.app.Activity;
import com.trianguloy.urlchecker.dialogs.MainDialog;
import com.trianguloy.urlchecker.fragments.Fragment;
import com.trianguloy.urlchecker.url.UrlData;
import com.trianguloy.urlchecker.utilities.Fragment;
/**
* Base class for a module's dialog fragment.

View File

@ -13,6 +13,7 @@ import com.trianguloy.urlchecker.modules.AModuleData;
import com.trianguloy.urlchecker.modules.AModuleDialog;
import com.trianguloy.urlchecker.modules.DescriptionConfig;
import com.trianguloy.urlchecker.url.UrlData;
import com.trianguloy.urlchecker.utilities.DoubleEvent;
/**
* This module shows the current url and allows manual editing
@ -42,9 +43,8 @@ public class TextInputModule extends AModuleData {
class TextInputDialog extends AModuleDialog implements TextWatcher {
private static final int SAME_UPDATE_TIMEOUT = 1000; // if two updates happens in less than this milliseconds, they are considered as the same
private final DoubleEvent doubleEdit = new DoubleEvent(1000); // if two updates happens in less than this milliseconds, they are considered as the same
private long lastUpdateTimeMillis = -1; // previous edittext update time
private EditText edtxt_url;
public TextInputDialog(MainDialog dialog) {
@ -68,7 +68,7 @@ class TextInputDialog extends AModuleDialog implements TextWatcher {
edtxt_url.removeTextChangedListener(this);
edtxt_url.setText(urlData.url);
edtxt_url.addTextChangedListener(this);
lastUpdateTimeMillis = -1; // next user update, even if immediately after, will be considered new
doubleEdit.reset(); // next user update, even if immediately after, will be considered new
}
// ------------------- TextWatcher -------------------
@ -84,16 +84,12 @@ class TextInputDialog extends AModuleDialog implements TextWatcher {
@Override
public void afterTextChanged(Editable s) {
// new url by the user
UrlData newUrlData = new UrlData(s.toString())
var newUrlData = new UrlData(s.toString())
.dontTriggerOwn()
.disableUpdates();
// mark as minor if too quick
long currentTime = System.currentTimeMillis();
if (currentTime - lastUpdateTimeMillis < SAME_UPDATE_TIMEOUT) {
newUrlData.asMinorUpdate();
}
lastUpdateTimeMillis = currentTime;
if (doubleEdit.checkAndTrigger()) newUrlData.asMinorUpdate();
// set
setUrl(newUrlData);

View File

@ -0,0 +1,52 @@
package com.trianguloy.urlchecker.utilities;
/**
* A small utility to detect repeated events in a short time
*/
public class DoubleEvent {
private long lastTime;
private final long delay;
/**
* Two events separated by [delayMillis] will be considered equal
*/
public DoubleEvent(long delayMillis) {
this.delay = delayMillis;
reset();
}
/**
* returns true if an event now happens less than delay from before
* Doesn't trigger an event
*/
public boolean check() {
return System.currentTimeMillis() - lastTime < delay;
}
/**
* Triggers an event
*/
public void trigger() {
lastTime = System.currentTimeMillis();
}
/**
* returns true if an event now happens less than delay from before
* Triggers an event afterward
*/
public boolean checkAndTrigger() {
var check = check();
trigger();
return check;
}
/**
* Resets the event trigger, next check will always return false
*/
public void reset() {
lastTime = -1;
}
}

View File

@ -12,52 +12,25 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/txt_openLinks" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<Button
android:id="@+id/b1"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="chooseBrowserPopup"
android:text="@string/btn_setBrowser" /><Button
android:id="@+id/b2"
<Button
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="openBrowserSettings"
android:text="@string/btn_configureBrowser" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<Button
android:id="@+id/b3"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="openAppLinks"
android:text="@string/btn_linksSettings" />
<Button
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="openAppDetails"
android:text="@string/btn_androidSettings" />
</LinearLayout>
android:onClick="openTutorial"
android:text="@string/btn_tutorialSettings" /><FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/padding">
<include layout="@layout/separator" />
</FrameLayout><TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/txt_openLinks" />
<include layout="@layout/fragment_browser_buttons" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -99,6 +72,6 @@
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,320 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activities.TutorialActivity">
<ViewFlipper
android:id="@+id/flipper"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_weight="1"
android:autoStart="false">
<ScrollView
android:id="@+id/p1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="@dimen/padding"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="64dp"
android:scaleType="fitCenter"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@string/app_name"
android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding"
android:gravity="center"
android:text="@string/txt_tutorial1" />
</LinearLayout>
</ScrollView>
<ScrollView
android:id="@+id/p2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="@dimen/padding"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/txt_tutorial2" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/padding" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="@dimen/smallPadding"
android:src="@android:mipmap/sym_def_app_icon" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@android:drawable/ic_menu_send" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/mOpen_open" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/smallPadding"
android:src="@android:drawable/ic_menu_share" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/share" />
</LinearLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="@dimen/smallPadding"
android:scaleType="fitCenter"
android:src="@mipmap/ic_launcher" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@android:drawable/ic_menu_send" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/mOpen_open" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/smallPadding"
android:src="@android:drawable/ic_menu_share" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/share" />
</LinearLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="@dimen/smallPadding"
android:src="@android:mipmap/sym_def_app_icon" />
</LinearLayout>
</LinearLayout>
</ScrollView>
<ScrollView
android:id="@+id/p3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="@dimen/padding"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/txt_tutorial3" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding"
android:gravity="center"
android:text="@string/txt_openLinks" />
<include layout="@layout/fragment_browser_buttons" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding"
android:autoLink="web"
android:clickable="true"
android:focusable="true"
android:gravity="center"
android:linksClickable="false"
android:text="@string/txt_sample" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:gravity="center"
android:minHeight="48dp"
android:padding="@dimen/smallPadding"
android:text="@string/sample_url" />
</LinearLayout>
</ScrollView>
<ScrollView
android:id="@+id/p4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="@dimen/padding"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/txt_tutorial4" />
<Button
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/padding"
android:drawableStart="@android:drawable/ic_menu_sort_by_size"
android:drawableLeft="@android:drawable/ic_menu_sort_by_size"
android:drawablePadding="@dimen/smallPadding"
android:onClick="openModulesActivity"
android:paddingLeft="@dimen/padding"
android:paddingRight="@dimen/padding"
android:text="@string/a_modules" />
</LinearLayout>
</ScrollView>
<ScrollView
android:id="@+id/p5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="@dimen/padding"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/txt_tutorial5" />
</LinearLayout>
</ScrollView>
</ViewFlipper>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/smallPadding"
android:layout_weight="0">
<Button
android:id="@+id/bBack"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="prev"
android:text="&lt;" />
<TextView
android:id="@+id/pageIndex"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_horizontal|center_vertical"
android:text="/" />
<Button
android:id="@+id/bNext"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="next"
android:text="&gt;" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/browser_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:context=".fragments.BrowserButtonsFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<Button
android:id="@+id/b1"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/btn_setBrowser" />
<Button
android:id="@+id/b2"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/btn_configureBrowser" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<Button
android:id="@+id/b3"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/btn_linksSettings" />
<Button
android:id="@+id/b4"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/btn_androidSettings" />
</LinearLayout>
</LinearLayout>

View File

@ -49,6 +49,26 @@ Traducciones: %s."</string>
<string name="txt_locale">Idioma:</string>
<!-- generic -->
<string name="tutorial">Tutorial</string>
<string name="txt_tutorial1">"¡Gracias por descargar la aplicación!
Este es el tutorial, si quieres saltarlo puedes hacerlo con el botón de debajo. Puedes volver a este tutorial siempre que quieras desde el menú de ajustes."</string>
<string name="txt_tutorial2">La aplicación actúa como intermediaria cuando se abre o comparte un enlace. Una vez se envía un enlace a la aplicación puedes interactuar con él mediante los módulos. Tras ello puedes abrir el enlace (en la aplicación que quieras), compartirlo o copiarlo al portapapeles.</string>
<string name="txt_tutorial3">Para que la aplicación pueda interceptar enlaces necesitamos configurarla como navegador por defecto. Esto no es necesario si piensas pasar los enlaces usando la opción de compartirlos.</string>
<string name="txt_tutorial4">"Los módulos son la forma en la que la app interactúa con los enlaces. Hay múltiples módulos, cada uno de ellos tiene una función específica, normalmente o te dan información del enlace, o te permiten modificarlo. Se pueden modificar bastante por lo que es recomendable que les eches un vistazo y los cambies como sea necesario. Cada uno está explicado allí.
Importante: todas las opciones son manuales por defecto, la aplicación no modificará nada ni se conectará a Internet a menos que lo permitas específicamente, pero algunas acciones se pueden configurar para actuar automáticamente si quieres.
Puedes abrir la lista de módulos desde la ventana principal, o pulsando el botón de debajo."</string>
<string name="txt_tutorial5">"Eso es todo. Aquí tienes algunos consejos que pueden ser útiles:
Cuando una app no te da una forma de abrir o compartir un enlace, pero sí te una forma para hacer la otra puedes solventar este problema usando esta aplicación. También puedes copiar el enlace al portapapeles al mantener el botón de compartir.
Hay algunos enlaces cuyo único propósito es redirigirte a otro enlace. Si el enlace final puede ser abierto en una aplicación, entonces puedes evitar abrirlo en un navegador.
¡Esperamos que la aplicación te resulte útil! No dudes en sugerir funcionalidades, reportar errores o proponer cambios. Puedes encontrar los todos los enlaces importantes en la pantalla 'Acerca de'."</string>
<string name="btn_tutorialSkip">Saltar tutorial</string>
<string name="btn_tutorialEnd">Terminar tutorial</string>
<string name="toast_noApp">No se puede abrir la aplicación</string>
<string name="toast_noBrowser">No se puede abrir el navegador</string>
<string name="title_choose">Elige aplicación</string>
@ -244,5 +264,8 @@ Para deshabilitar el registro, deshabilita el módulo."</string>
<string name="mHosts_buildDownload">"Descargando '%s' hosts de %s…"</string>
<string name="mHosts_buildSave">Guardando…</string>
<string name="mHosts_built">Base de datos construida: %d hosts</string>
<string name="btn_tutorialSettings">Repetir tutorial</string>
<string name="next">Siguiente</string>
<string name="back">Anterior</string>
</resources>

View File

@ -46,6 +46,28 @@ Traduction: %s."</string>
<string name="spin_lightTheme">Clair</string>
<string name="txt_locale">Langue:</string>
<!-- tutorial -->
<string name="tutorial">Didacticiel</string>
<string name="txt_tutorial1">"Merci d'avoir téléchargé l'application!
Ceci est le didacticiel, si vous souhaitez l'ignorer vous pouvez le faire en utilisant le bouton ci-dessous. Vous pourrez toujours revenir à ce didacticiel à partir du menu paramètres."</string>
<string name="txt_tutorial2">"L'application agit comme intermédiaire lors de l'ouverture ou du partage de liens URL. Une fois qu'une URL est envoyée à l'application, vous pouvez interagir avec elle via les modules. Après cela vous pouvez ouvrir le lien (dans l'application de votre choix), le partager ou le copier dans le presse-papiers."</string>
<string name="txt_tutorial3">"Pour que l'application intercepte les URL lors de leur ouverture, nous devons d'abord la configurer comme navigateur par défaut. Cela n'est pas nécessaire si vous avez l'intention de transmettre les URL à l'aide de la fonctionnalité Partager."</string>
<string name="txt_tutorial4">"Les modules sont la façon dont l'application interagit avec les URL. Il existe plusieurs modules, chacun d'eux avec une fonction spécifique, généralement ils vous fournissent des informations sur l'url, ou permettent de la modifier. Chacun dispose d'options, il est donc recommandé de vérifier et d'adapter leurs configurations au besoin. Les fonctionnalités de chaque modules y est expliqué.
Important: toutes les actions sont manuelles par défaut, l'application ne modifiera rien et ne se connectera pas à Internet à moins que vous ne l'y autorisiez explicitement, mais certaines actions peuvent être configurées pour s'appliquer automatiquement si vous le souhaitez.
Vous pouvez ouvrir la liste des modules depuis l'écran principal ou en appuyant sur le bouton ci-dessous."</string>
<string name="txt_tutorial5">"C'est presque tout. Voici quelques conseils qui peuvent être utiles:
Lorsqu'une application ne vous permet pas d'ouvrir ou de partager une URL, mais qu'elle vous permet autre chose, vous pouvez résoudre le problème à l'aide de cette application. Vous pouvez également copier l'url dans le presse-papiers en appuyant longuement sur le bouton Partager.
Certaines URL ont pour seul but de vous rediriger vers une autre URL. Si l'url finale peut être ouverte dans une application, alors vous pouvez éviter de l'ouvrir dans un navigateur.
J'espère que vous trouverez l'application utile! Et n'hésitez pas à suggérer des fonctionnalités, à signaler des bugs ou même à proposer des changements. Trouvez des liens pertinents sur l'écran À propos."</string>
<string name="btn_tutorialSkip">Ignorer le didacticiel</string>
<string name="btn_tutorialEnd">Clore le didacticiel</string>
<!-- generic -->
<string name="toast_noApp">"Impossible d'ouvrir l'application"</string>
<string name="toast_noBrowser">"Impossible d'ouvrir le navigateur"</string>

View File

@ -45,6 +45,29 @@ Translations: %s."</string>
<string name="spin_darkTheme">Dark</string>
<string name="spin_lightTheme">Light</string>
<string name="txt_locale">Locale:</string>
<string name="btn_tutorialSettings">Repeat tutorial</string>
<!-- tutorial -->
<string name="tutorial">Tutorial</string>
<string name="txt_tutorial1">"Thanks for downloading the app!
This is the tutorial, if you want to skip it you can do so with the button below. You can always come back to this tutorial from the settings menu."</string>
<string name="txt_tutorial2">The app acts as an intermediary when opening or sharing url links. Once an url is sent to the app you can interact with it via the modules. After that you can open the link (in the app you want), share it or copy it to the clipboard.</string>
<string name="txt_tutorial3">For the app to intercept the urls when opening them we need to set it up as the default browser first. This is not needed if you intent to pass the urls using the share functionality.</string>
<string name="txt_tutorial4">"The modules are the way the app interacts with the urls. There are multiple modules, each of them with a specific function, usually they either give you information about the url, or allow to modify it. They are quite configurable so it is recommended you check them and adapt as needed. Each one is explained there.
Important: all the actions are manual by default, the app won't modify anything nor connect to the Internet unless you specifically allow it, but some actions can be configured to apply automatically if desired.
You can open the list of modules from the main screen, or by tapping the button below."</string>
<string name="txt_tutorial5">"That's all. Here are some tips that can be useful:
When an app doesn't give you a way to open or share a url, but it gives you a way to do the other you can solve the issue using this app. You can also copy the url to the clipboard by long pressing the share button.
There are some urls which its only purpose is redirecting you to another url. If the final url can be opened in an app, then you can avoid opening it in a browser.
Hope you find the app useful! And don't hesitate to suggest features, report bugs or even propose changes. Find all relevant links on the about screen."</string>
<string name="btn_tutorialSkip">Skip tutorial</string>
<string name="btn_tutorialEnd">Finish tutorial</string>
<!-- generic -->
<string name="toast_noApp">"Can't open application"</string>
@ -59,6 +82,8 @@ Translations: %s."</string>
<string name="share">Share</string>
<string name="canceled">Canceled</string>
<string name="deviceDefault">Device default</string>
<string name="next">Next</string>
<string name="back">Back</string>
<string name="json_desc">[Beta feature] This is an advanced editor, the content must be formatted into valid JSON. You can press the top-right button to format and validate it.</string>