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

Fix #2209 #2211

Merged
merged 6 commits into from
May 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ apply plugin: 'com.github.ben-manes.versions'

buildscript {
ext {
javaVersion = JavaVersion.VERSION_1_8
kotlinVersion = '1.3.31'
minSdkVersion = 21
sdkVersion = 28
Expand Down
6 changes: 6 additions & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ android {
}
}

compileOptions {
sourceCompatibility javaVersion
targetCompatibility javaVersion
}

externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'
Expand All @@ -47,6 +52,7 @@ def lifecycleVersion = '2.0.0'
def roomVersion = '2.0.0'
dependencies {
api project(':plugin')
api "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion"
api "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
api "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
api 'androidx.preference:preference:1.1.0-alpha05'
Expand Down
15 changes: 13 additions & 2 deletions core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,23 @@
android:exported="false">
</service>

<activity
android:name="com.github.shadowsocks.UrlImportActivity"
android:theme="@style/Theme.AppCompat.Translucent"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="ss"/>
</intent-filter>
</activity>

<activity
android:name="com.github.shadowsocks.VpnRequestActivity"
android:theme="@style/Theme.AppCompat.Translucent"
android:excludeFromRecents="true"
android:taskAffinity=""
android:launchMode="singleTask"/>
android:taskAffinity=""/>

<receiver android:name="com.github.shadowsocks.BootReceiver"
android:process=":bg"
Expand Down
72 changes: 72 additions & 0 deletions core/src/main/java/com/github/shadowsocks/UrlImportActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*******************************************************************************
* *
* Copyright (C) 2019 by Max Lv <[email protected]> *
* Copyright (C) 2019 by Mygod Studio <[email protected]> *
* *
* 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 com.github.shadowsocks

import android.content.DialogInterface
import android.os.Bundle
import android.os.Parcelable
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.github.shadowsocks.core.R
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.plugin.AlertDialogFragment
import com.github.shadowsocks.plugin.Empty
import kotlinx.android.parcel.Parcelize

class UrlImportActivity : AppCompatActivity() {
@Parcelize
data class ProfilesArg(val profiles: List<Profile>) : Parcelable
class ImportProfilesDialogFragment : AlertDialogFragment<ProfilesArg, Empty>() {
override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) {
setTitle(R.string.add_profile_dialog)
setPositiveButton(R.string.yes, listener)
setNegativeButton(R.string.no, listener)
setMessage(arg.profiles.joinToString("\n"))
}

override fun onClick(dialog: DialogInterface?, which: Int) {
if (which == DialogInterface.BUTTON_POSITIVE) arg.profiles.forEach { ProfileManager.createProfile(it) }
requireActivity().finish()
}

override fun onDismiss(dialog: DialogInterface) {
requireActivity().finish()
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
when (val dialog = handleShareIntent()) {
null -> {
Toast.makeText(this, R.string.profile_invalid_input, Toast.LENGTH_SHORT).show()
finish()
}
else -> dialog.show(supportFragmentManager, null)
}
}

private fun handleShareIntent() = intent.data?.toString()?.let { sharedStr ->
val profiles = Profile.findAllUrls(sharedStr, Core.currentProfile?.first).toList()
if (profiles.isEmpty()) null else ImportProfilesDialogFragment().withArg(ProfilesArg(profiles))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*******************************************************************************
* *
* Copyright (C) 2019 by Max Lv <[email protected]> *
* Copyright (C) 2019 by Mygod Studio <[email protected]> *
* *
* 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 com.github.shadowsocks.utils

import androidx.activity.ComponentActivity
import androidx.annotation.MainThread
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner

/**
* See also: https://stackoverflow.com/a/30821062/2245107
*/
object SingleInstanceActivity : DefaultLifecycleObserver {
private val active = mutableSetOf<Class<LifecycleOwner>>()

@MainThread
fun register(activity: ComponentActivity) = if (active.add(activity.javaClass)) apply {
activity.lifecycle.addObserver(this)
} else {
activity.finish()
null
}

override fun onDestroy(owner: LifecycleOwner) {
check(active.remove(owner.javaClass)) { "Double destroy?" }
}
}
2 changes: 1 addition & 1 deletion core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
<string name="profile_config">Profile config</string>
<string name="delete">Remove</string>
<string name="delete_confirm_prompt">Are you sure you want to remove this profile?</string>
<string name="share_qr_nfc">QR code/NFC</string>
<string name="share_qr_nfc">QR code</string>
<string name="add_profile_dialog">Add this Shadowsocks Profile?</string>
<string name="add_profile_methods_scan_qr_code">Scan QR code</string>
<string name="add_profile_methods_manual_settings">Manual Settings</string>
Expand Down
4 changes: 4 additions & 0 deletions mobile/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility javaVersion
targetCompatibility javaVersion
}
packagingOptions.exclude '**/*.kotlin_*'
splits {
abi {
Expand Down
28 changes: 5 additions & 23 deletions mobile/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@
package="com.github.shadowsocks"
tools:ignore="MissingLeanbackSupport">

<uses-permission android:name="android.permission.NFC" />
<uses-permission-sdk-23 android:name="android.permission.CAMERA" />

<uses-feature android:name="android.hardware.touchscreen"
android:required="false"/>
<uses-feature android:name="android.hardware.nfc"
android:required="false"/>
<uses-feature android:name="android.hardware.camera"
android:required="false"/>

Expand All @@ -21,7 +18,7 @@
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/Theme.Shadowsocks.Navigation"
android:launchMode="singleTask">
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
Expand All @@ -30,40 +27,26 @@
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="ss"/>
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="ss" />
</intent-filter>
<meta-data android:name="android.app.shortcuts"
android:resource="@xml/shortcuts"/>
</activity>

<activity
android:name=".ProfileConfigActivity"
android:excludeFromRecents="true"
android:label="@string/profile_config"
android:launchMode="singleTask"/>
android:label="@string/profile_config"/>

<activity
android:name=".AppManager"
android:label="@string/proxied_apps"
android:parentActivityName=".ProfileConfigActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"/>
android:excludeFromRecents="true"/>

<activity
android:name=".UdpFallbackProfileActivity"
android:label="@string/udp_fallback"
android:parentActivityName=".ProfileConfigActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"/>
android:excludeFromRecents="true"/>

<activity
android:name=".ScannerActivity"
Expand All @@ -84,8 +67,7 @@
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:excludeFromRecents="true"
android:taskAffinity=""
android:process=":bg"
android:launchMode="singleTask">
android:process=":bg">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
</intent-filter>
Expand Down
2 changes: 2 additions & 0 deletions mobile/src/main/java/com/github/shadowsocks/AppManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.DirectBoot
import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.SingleInstanceActivity
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.layout_apps.*
import kotlinx.android.synthetic.main.layout_apps_item.view.*
Expand Down Expand Up @@ -208,6 +209,7 @@ class AppManager : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
SingleInstanceActivity.register(this) ?: return
setContentView(R.layout.layout_apps)
setSupportActionBar(toolbar)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
Expand Down
46 changes: 2 additions & 44 deletions mobile/src/main/java/com/github/shadowsocks/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,15 @@ package com.github.shadowsocks
import android.app.Activity
import android.app.backup.BackupManager
import android.content.ActivityNotFoundException
import android.content.DialogInterface
import android.content.Intent
import android.net.VpnService
import android.nfc.NdefMessage
import android.nfc.NfcAdapter
import android.os.Bundle
import android.os.DeadObjectException
import android.os.Handler
import android.os.Parcelable
import android.util.Log
import android.view.KeyCharacterMap
import android.view.KeyEvent
import android.view.MenuItem
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabsIntent
import androidx.coordinatorlayout.widget.CoordinatorLayout
Expand All @@ -51,18 +46,14 @@ import com.github.shadowsocks.aidl.IShadowsocksService
import com.github.shadowsocks.aidl.ShadowsocksConnection
import com.github.shadowsocks.aidl.TrafficStats
import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.plugin.AlertDialogFragment
import com.github.shadowsocks.plugin.Empty
import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener
import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.SingleInstanceActivity
import com.github.shadowsocks.widget.ServiceButton
import com.github.shadowsocks.widget.StatsBar
import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.parcel.Parcelize

class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPreferenceDataStoreChangeListener,
NavigationView.OnNavigationItemSelectedListener {
Expand All @@ -73,17 +64,6 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref
var stateListener: ((BaseService.State) -> Unit)? = null
}

@Parcelize
data class ProfilesArg(val profiles: List<Profile>) : Parcelable
class ImportProfilesDialogFragment : AlertDialogFragment<ProfilesArg, Empty>() {
override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) {
setTitle(R.string.add_profile_dialog)
setPositiveButton(R.string.yes) { _, _ -> arg.profiles.forEach { ProfileManager.createProfile(it) } }
setNegativeButton(R.string.no, null)
setMessage(arg.profiles.joinToString("\n"))
}
}

// UI
private lateinit var fab: ServiceButton
private lateinit var stats: StatsBar
Expand Down Expand Up @@ -167,6 +147,7 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
SingleInstanceActivity.register(this) ?: return
setContentView(R.layout.layout_main)
stats = findViewById(R.id.stats)
stats.setOnClickListener { if (state == BaseService.State.Connected) stats.testConnection() }
Expand All @@ -184,29 +165,6 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref
changeState(BaseService.State.Idle) // reset everything to init state
connection.connect(this, this)
DataStore.publicStore.registerChangeListener(this)

val intent = this.intent
if (intent != null) handleShareIntent(intent)
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleShareIntent(intent)
}
private fun handleShareIntent(intent: Intent) {
val sharedStr = when (intent.action) {
Intent.ACTION_VIEW -> intent.data?.toString()
NfcAdapter.ACTION_NDEF_DISCOVERED -> {
val rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
if (rawMsgs != null && rawMsgs.isNotEmpty()) String((rawMsgs[0] as NdefMessage).records[0].payload)
else null
}
else -> null
}
if (sharedStr.isNullOrEmpty()) return
val profiles = Profile.findAllUrls(sharedStr, Core.currentProfile?.first).toList()
if (profiles.isEmpty()) snackbar().setText(R.string.profile_invalid_input).show()
else ImportProfilesDialogFragment().withArg(ProfilesArg(profiles)).show(supportFragmentManager, null)
}

override fun onPreferenceDataStoreChanged(store: PreferenceDataStore, key: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.github.shadowsocks.plugin.AlertDialogFragment
import com.github.shadowsocks.plugin.Empty
import com.github.shadowsocks.plugin.PluginContract
import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.SingleInstanceActivity

class ProfileConfigActivity : AppCompatActivity() {
companion object {
Expand All @@ -51,6 +52,7 @@ class ProfileConfigActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
SingleInstanceActivity.register(this) ?: return
setContentView(R.layout.layout_profile_config)
setSupportActionBar(findViewById(R.id.toolbar))
supportActionBar!!.apply {
Expand Down
Loading