Commit dbb137c5 authored by Mygod's avatar Mygod

Initial redesign draft for #762

parent 7eddf8be
...@@ -31,17 +31,18 @@ resConfigs := Seq("ja", "ru", "zh-rCN", "zh-rTW") ...@@ -31,17 +31,18 @@ resConfigs := Seq("ja", "ru", "zh-rCN", "zh-rTW")
resolvers += Resolver.jcenterRepo resolvers += Resolver.jcenterRepo
libraryDependencies ++= libraryDependencies ++=
"com.android.support" % "cardview-v7" % "25.1.0" ::
"com.android.support" % "design" % "25.1.0" :: "com.android.support" % "design" % "25.1.0" ::
"com.android.support" % "gridlayout-v7" % "25.1.0" :: "com.android.support" % "gridlayout-v7" % "25.1.0" ::
"com.android.support" % "preference-v14" % "25.1.0" :: "com.android.support" % "preference-v14" % "25.1.0" ::
"com.evernote" % "android-job" % "1.1.3" :: "com.evernote" % "android-job" % "1.1.3" ::
"com.github.clans" % "fab" % "1.6.4" ::
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01" :: "com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01" ::
"com.github.kevinsawicki" % "http-request" % "6.0" :: "com.github.kevinsawicki" % "http-request" % "6.0" ::
"com.google.android.gms" % "play-services-ads" % "10.0.1" :: "com.google.android.gms" % "play-services-ads" % "10.0.1" ::
"com.google.android.gms" % "play-services-analytics" % "10.0.1" :: "com.google.android.gms" % "play-services-analytics" % "10.0.1" ::
"com.google.android.gms" % "play-services-gcm" % "10.0.1" :: "com.google.android.gms" % "play-services-gcm" % "10.0.1" ::
"com.j256.ormlite" % "ormlite-android" % "5.0" :: "com.j256.ormlite" % "ormlite-android" % "5.0" ::
"com.mikepenz" % "materialdrawer" % "5.8.1" ::
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2" :: "com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2" ::
"dnsjava" % "dnsjava" % "2.1.7" :: "dnsjava" % "dnsjava" % "2.1.7" ::
"eu.chainfire" % "libsuperuser" % "1.0.0.201608240809" :: "eu.chainfire" % "libsuperuser" % "1.0.0.201608240809" ::
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
android:value="AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg"/> android:value="AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg"/>
<activity <activity
android:name=".Shadowsocks" android:name=".MainActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleTask"> android:launchMode="singleTask">
<intent-filter> <intent-filter>
...@@ -49,28 +49,6 @@ ...@@ -49,28 +49,6 @@
<intent-filter> <intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" /> <action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
</intent-filter> </intent-filter>
<meta-data android:name="android.app.shortcuts"
android:resource="@xml/shortcuts"/>
</activity>
<activity
android:name=".ShadowsocksRunnerActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:excludeFromRecents="true"
android:taskAffinity=""
android:launchMode="singleTask">
</activity>
<activity
android:name=".ProfileManagerActivity"
android:label="@string/profiles"
android:parentActivityName=".Shadowsocks"
android:excludeFromRecents="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="com.github.shadowsocks.ProfileManagerActivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW"/> <action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
...@@ -82,18 +60,34 @@ ...@@ -82,18 +60,34 @@
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="ss" /> <data android:scheme="ss" />
</intent-filter> </intent-filter>
<meta-data android:name="android.app.shortcuts"
android:resource="@xml/shortcuts"/>
</activity> </activity>
<activity
android:name=".ProfileConfigActivity"
android:excludeFromRecents="true"
android:label="Profile config"
android:launchMode="singleTask"
android:parentActivityName=".MainActivity"/>
<activity
android:name=".ShadowsocksRunnerActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:excludeFromRecents="true"
android:taskAffinity=""
android:launchMode="singleTask"/>
<activity <activity
android:name=".ScannerActivity" android:name=".ScannerActivity"
android:label="@string/add_profile_methods_scan_qr_code" android:label="@string/add_profile_methods_scan_qr_code"
android:parentActivityName=".ProfileManagerActivity" android:parentActivityName=".MainActivity"
android:excludeFromRecents="true"/> android:excludeFromRecents="true"/>
<activity <activity
android:name=".AppManager" android:name=".AppManager"
android:label="@string/proxied_apps" android:label="@string/proxied_apps"
android:parentActivityName=".Shadowsocks" android:parentActivityName=".ProfileConfigActivity"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:launchMode="singleTask"/> android:launchMode="singleTask"/>
......
package com.github.shadowsocks;
import android.app.Activity;
/**
* This helper class is used to solve Java-Scala interop problem. Use onAttach(Context) and remove this class
* when minSdkVersion >= 23.
*
* @author Mygod
*/
public class DialogFragment extends android.app.DialogFragment {
protected void superOnAttach(Activity activity) {
super.onAttach(activity);
}
}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
</vector>
...@@ -5,5 +5,5 @@ ...@@ -5,5 +5,5 @@
android:viewportHeight="24.0"> android:viewportHeight="24.0">
<path <path
android:fillColor="#FF000000" android:fillColor="#FF000000"
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/> android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector> </vector>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/toolbar_light_dark" />
<fragment android:id="@+id/content"
class="com.github.shadowsocks.GlobalConfigFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
This diff is collapsed.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/toolbar_light_dark" />
<fragment android:id="@+id/content"
class="com.github.shadowsocks.ProfileConfigFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:orientation="vertical" android:layout_height="match_parent"
android:layout_height="match_parent"> android:orientation="vertical">
<include layout="@layout/toolbar_light_dark" /> <include layout="@layout/toolbar_light_dark"/>
<android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="0dp" android:layout_weight="1"> android:id="@+id/profilesList"
<android.support.v7.widget.RecyclerView android:id="@+id/profilesList" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="0dp"
android:layout_height="match_parent"/> android:layout_weight="1"
<com.github.clans.fab.FloatingActionMenu android:paddingBottom="8dp"/>
android:id="@+id/menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom|end"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
app:menu_colorNormal="@color/material_primary_500"
app:menu_colorPressed="@color/material_primary_700"
app:fab_colorRipple="@android:color/white"
app:layout_behavior="com.github.shadowsocks.widget.FloatingActionMenuBehavior">
<com.github.clans.fab.FloatingActionButton
android:id="@+id/fab_manual_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_colorNormal="@color/material_primary_500"
app:fab_colorPressed="@color/material_primary_700"
app:fab_colorRipple="@android:color/white"
app:fab_size="mini"
app:fab_label="@string/add_profile_methods_manual_settings"
/>
<com.github.clans.fab.FloatingActionButton
android:id="@+id/fab_qrcode_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_colorNormal="@color/material_primary_500"
app:fab_colorPressed="@color/material_primary_700"
app:fab_colorRipple="@android:color/white"
app:fab_size="mini"
app:fab_label="@string/add_profile_methods_scan_qr_code"
/>
<com.github.clans.fab.FloatingActionButton
android:id="@+id/fab_nfc_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_colorNormal="@color/material_primary_500"
app:fab_colorPressed="@color/material_primary_700"
app:fab_colorRipple="@android:color/white"
app:fab_size="mini"
app:fab_label="NFC"
/>
<com.github.clans.fab.FloatingActionButton
android:id="@+id/fab_import_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_colorNormal="@color/material_primary_500"
app:fab_colorPressed="@color/material_primary_700"
app:fab_colorRipple="@android:color/white"
app:fab_size="mini"
app:fab_label="@string/action_import"
/>
</com.github.clans.fab.FloatingActionMenu>
</android.support.design.widget.CoordinatorLayout>
</LinearLayout> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Based on: https://github.com/android/platform_frameworks_base/blob/master/core/res/res/layout/select_dialog_singlechoice_material.xml --> <!-- Based on: https://github.com/android/platform_frameworks_base/blob/505e3ab/core/res/res/layout/simple_list_item_2.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/container" android:background="?android:attr/selectableItemBackground"
android:background="?android:attr/selectableItemBackground" android:focusable="true"
android:paddingStart="20dp" android:nextFocusRight="@+id/edit"
android:paddingEnd="?attr/dialogPreferredPadding" android:layout_marginTop="8dp"
android:focusable="true" android:layout_marginStart="8dp"
android:nextFocusRight="@+id/share"> android:layout_marginEnd="8dp">
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout android:layout_width="match_parent"
android:id="@android:id/text1" android:layout_height="wrap_content"
android:layout_width="0dp" android:orientation="vertical"
android:layout_height="wrap_content" android:layout_margin="8dp">
android:layout_gravity="center_vertical" <LinearLayout android:layout_width="match_parent"
android:layout_weight="1" android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall" android:id="@+id/container">
android:textAppearance="?android:attr/textAppearanceMedium" <TextView android:id="@android:id/text1"
android:textColor="?android:attr/textColorAlertDialogListItem" android:layout_width="0dp"
android:gravity="center_vertical" android:layout_height="wrap_content"
android:drawableStart="?android:attr/listChoiceIndicatorSingle" android:layout_gravity="center_vertical"
android:drawablePadding="20dp" android:layout_weight="1"
android:maxLines="4" android:textAppearance="?android:attr/textAppearanceLarge"
android:scrollHorizontally="false" /> android:textColor="?android:attr/textColorPrimary"
<ImageView android:id="@+id/share" android:gravity="center_vertical"
android:layout_width="wrap_content" android:maxLines="2"
android:layout_height="wrap_content" android:ellipsize="end"/>
android:padding="5dp" <ImageView android:id="@+id/edit"
android:layout_gravity="center_vertical" android:layout_width="wrap_content"
app:srcCompat="@drawable/ic_social_share" android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless" android:padding="4dp"
android:contentDescription="@string/share" android:layout_gravity="top"
android:focusable="true" app:srcCompat="@drawable/ic_image_edit"
android:nextFocusLeft="@+id/container"/> android:background="?attr/selectableItemBackgroundBorderless"
</LinearLayout> android:contentDescription="Edit"
android:focusable="true"
android:nextFocusLeft="@+id/container"/>
</LinearLayout>
<TextView android:id="@android:id/text2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorSecondary"/>
</LinearLayout>
</android.support.v7.widget.CardView>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:title="@string/share"
android:icon="@drawable/abc_ic_menu_share_mtrl_alpha"
android:alphabeticShortcut="s"
android:numericShortcut="1"
app:showAsAction="always">
<menu>
<item android:id="@+id/action_qr_code_nfc"
android:title="QR code/NFC"
android:alphabeticShortcut="q"
android:numericShortcut="1"/>
<item android:id="@+id/action_export"
android:title="@string/action_export"
android:alphabeticShortcut="e"
android:numericShortcut="2"/>
</menu>
</item>
<item android:id="@+id/action_delete"
android:title="Delete"
android:icon="@drawable/ic_action_delete"
android:alphabeticShortcut="d"
android:numericShortcut="2"
app:showAsAction="always"/>
<item android:id="@+id/action_apply"
android:title="Apply"
android:icon="@drawable/ic_action_done"
android:alphabeticShortcut="a"
android:numericShortcut="3"
app:showAsAction="always"/>
</menu>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:title="@string/add_profile"
android:icon="@drawable/ic_content_add"
android:alphabeticShortcut="a"
android:numericShortcut="0"
app:showAsAction="always">
<menu>
<item android:id="@+id/action_scan_qr_code"
android:title="@string/add_profile_methods_scan_qr_code"
android:alphabeticShortcut="s"
android:numericShortcut="1"/>
<item android:id="@+id/action_import"
android:title="@string/action_import"
android:alphabeticShortcut="i"
android:numericShortcut="2"/>
<item android:id="@+id/action_manual_settings"
android:title="@string/add_profile_methods_manual_settings"
android:alphabeticShortcut="m"
android:numericShortcut="3"/>
</menu>
</item>
<item android:id="@+id/action_export" <item android:id="@+id/action_export"
android:title="@string/action_export" android:title="@string/action_export"
android:alphabeticShortcut="e" android:alphabeticShortcut="e"
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<dimen name="fab_margin_bottom">-28dp</dimen> <dimen name="fab_margin_top">-28dp</dimen>
</resources> </resources>
...@@ -3,12 +3,13 @@ ...@@ -3,12 +3,13 @@
<resources> <resources>
<color name="material_green_700">#388E3C</color> <color name="material_green_700">#388E3C</color>
<color name="material_green_a700">#00C853</color>
<color name="material_blue_grey_300">#90A4AE</color> <color name="material_blue_grey_300">#90A4AE</color>
<color name="material_blue_grey_500">#607D8B</color> <color name="material_blue_grey_500">#607D8B</color>
<color name="material_blue_grey_700">#455A64</color> <color name="material_blue_grey_700">#455A64</color>
<color name="material_primary_300">@color/material_blue_grey_300</color> <color name="material_primary_300">@color/material_blue_grey_300</color>
<color name="material_primary_500">@color/material_blue_grey_500</color> <color name="material_primary_500">@color/material_blue_grey_500</color>
<color name="material_primary_700">@color/material_blue_grey_700</color> <color name="material_primary_700">@color/material_blue_grey_700</color>
<color name="material_accent_200">@color/material_blue_grey_500</color> <color name="material_accent_200">@color/material_green_a700</color>
</resources> </resources>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- TODO: Remove when this issue is fixed: https://github.com/JorgeCastilloPrz/FABProgressCircle/issues/19 --> <!-- TODO: Remove when this issue is fixed: https://github.com/JorgeCastilloPrz/FABProgressCircle/issues/19 -->
<dimen name="fab_margin_bottom">-44dp</dimen> <dimen name="fab_margin_top">-44dp</dimen>
</resources> </resources>
...@@ -6,11 +6,11 @@ ...@@ -6,11 +6,11 @@
<item name="android:textSize">24sp</item> <item name="android:textSize">24sp</item>
</style> </style>
<style name="Theme.MaterialBase" parent="Theme.AppCompat.Light.NoActionBar"> <style name="Theme.MaterialBase" parent="MaterialDrawerTheme.Light.DarkToolbar">
<!--suppress NewApi --> <!--suppress NewApi -->
<item name="android:navigationBarColor">@color/material_primary_700</item> <item name="android:navigationBarColor">@color/material_primary_700</item>
<item name="colorAccent">@color/material_primary_500</item> <item name="colorAccent">@color/material_primary_500</item>
<item name="colorButtonNormal">@color/material_primary_500</item> <item name="colorButtonNormal">@color/material_accent_200</item>
<item name="colorPrimary">@color/material_primary_500</item> <item name="colorPrimary">@color/material_primary_500</item>
<item name="colorPrimaryDark">@color/material_primary_700</item> <item name="colorPrimaryDark">@color/material_primary_700</item>
<item name="preferenceTheme">@style/Theme.Material.PreferenceThemeOverlay</item> <item name="preferenceTheme">@style/Theme.Material.PreferenceThemeOverlay</item>
......
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference android:key="isAutoConnect"
android:persistent="false"
android:summary="@string/auto_connect_summary"
android:title="@string/auto_connect"/>
<SwitchPreference android:key="tcp_fastopen"
android:summary="@string/tcp_fastopen_summary"
android:title="TCP Fast Open"/>
<SwitchPreference android:key="isNAT"
android:title="@string/nat"
android:summary="@string/nat_summary"/>
<Preference android:key="recovery"
android:title="@string/recovery"
android:summary="@string/recovery_summary"/>
</PreferenceScreen>
...@@ -7,44 +7,37 @@ ...@@ -7,44 +7,37 @@
<be.mygod.preference.EditTextPreference <be.mygod.preference.EditTextPreference
android:key="profileName" android:key="profileName"
android:persistent="false"
android:summary="%s" android:summary="%s"
android:title="@string/profile_name"/> android:title="@string/profile_name"/>
<be.mygod.preference.EditTextPreference <be.mygod.preference.EditTextPreference
android:key="proxy" android:key="proxy"
android:persistent="false"
android:summary="%s" android:summary="%s"
android:title="@string/proxy"/> android:title="@string/proxy"/>
<be.mygod.preference.NumberPickerPreference <be.mygod.preference.NumberPickerPreference
app:min="1" app:min="1"
app:max="65535" app:max="65535"
android:key="remotePortNum" android:key="remotePortNum"
android:persistent="false"
android:summary="%d" android:summary="%d"
android:title="@string/remote_port"/> android:title="@string/remote_port"/>
<be.mygod.preference.NumberPickerPreference <be.mygod.preference.NumberPickerPreference
app:min="1025" app:min="1025"
app:max="65535" app:max="65535"
android:key="localPortNum" android:key="localPortNum"
android:persistent="false"
android:summary="%d" android:summary="%d"
android:title="@string/port"/> android:title="@string/port"/>
<be.mygod.preference.EditTextPreference <be.mygod.preference.EditTextPreference
android:inputType="textPassword" android:inputType="textPassword"
android:key="sitekey" android:key="sitekey"
android:persistent="false"
android:summary="%s" android:summary="%s"
android:title="@string/sitekey"/> android:title="@string/sitekey"/>
<DropDownPreference <DropDownPreference
android:key="encMethod" android:key="encMethod"
android:persistent="false"
android:entries="@array/enc_method_entry" android:entries="@array/enc_method_entry"
android:entryValues="@array/enc_method_value" android:entryValues="@array/enc_method_value"
android:summary="%s" android:summary="%s"
android:title="@string/enc_method"/> android:title="@string/enc_method"/>
<SwitchPreference <SwitchPreference
android:key="isAuth" android:key="isAuth"
android:persistent="false"
android:summary="@string/onetime_auth_summary" android:summary="@string/onetime_auth_summary"
android:title="@string/onetime_auth"/> android:title="@string/onetime_auth"/>
...@@ -55,7 +48,6 @@ ...@@ -55,7 +48,6 @@
<DropDownPreference <DropDownPreference
android:key="route" android:key="route"
android:persistent="false"
android:entries="@array/route_entry" android:entries="@array/route_entry"
android:entryValues="@array/route_value" android:entryValues="@array/route_value"
android:summary="%s" android:summary="%s"
...@@ -63,20 +55,17 @@ ...@@ -63,20 +55,17 @@
<be.mygod.preference.EditTextPreference <be.mygod.preference.EditTextPreference
android:key="remoteDns" android:key="remoteDns"
android:title="@string/remote_dns" android:title="@string/remote_dns"
android:summary="@string/remote_dns_summary"/> android:summary="%s"/>
<SwitchPreference <SwitchPreference
android:key="isIpv6" android:key="isIpv6"
android:persistent="false"
android:summary="@string/ipv6_summary" android:summary="@string/ipv6_summary"
android:title="@string/ipv6"/> android:title="@string/ipv6"/>
<SwitchPreference <SwitchPreference
android:key="isProxyApps" android:key="isProxyApps"
android:persistent="false"
android:summary="@string/proxied_apps_summary" android:summary="@string/proxied_apps_summary"
android:title="@string/proxied_apps"/> android:title="@string/proxied_apps"/>
<SwitchPreference <SwitchPreference
android:key="isUdpDns" android:key="isUdpDns"
android:persistent="false"
android:summary="@string/udp_dns_summary" android:summary="@string/udp_dns_summary"
android:title="@string/udp_dns"/> android:title="@string/udp_dns"/>
...@@ -87,7 +76,6 @@ ...@@ -87,7 +76,6 @@
<SwitchPreference <SwitchPreference
android:key="kcp" android:key="kcp"
android:persistent="false"
android:summary="@string/kcp_summary" android:summary="@string/kcp_summary"
android:title="@string/kcp"/> android:title="@string/kcp"/>
...@@ -95,41 +83,14 @@ ...@@ -95,41 +83,14 @@
app:min="1" app:min="1"
app:max="65535" app:max="65535"
android:key="kcpPort" android:key="kcpPort"
android:persistent="false"
android:summary="@string/kcp_port_summary" android:summary="@string/kcp_port_summary"
android:title="@string/kcp_port"/> android:title="@string/kcp_port"/>
<be.mygod.preference.EditTextPreference <be.mygod.preference.EditTextPreference
android:key="kcpcli" android:key="kcpcli"
android:persistent="false"
android:summary="%s" android:summary="%s"
android:title="@string/kcpcli"/> android:title="@string/kcpcli"/>
</be.mygod.preference.PreferenceCategory> </be.mygod.preference.PreferenceCategory>
<be.mygod.preference.PreferenceCategory android:title="@string/misc_cat">
<SwitchPreference
android:key="isAutoConnect"
android:persistent="false"
android:summary="@string/auto_connect_summary"
android:title="@string/auto_connect"/>
<SwitchPreference
android:key="tcp_fastopen"
android:summary="@string/tcp_fastopen_summary"
android:title="TCP Fast Open"/>
<SwitchPreference android:key="isNAT"
android:title="@string/nat"
android:summary="@string/nat_summary"/>
<Preference android:key="recovery" android:title="@string/recovery"
android:summary="@string/recovery_summary"/>
<Preference android:key="about" android:title="@string/about"/>
</be.mygod.preference.PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>
...@@ -98,10 +98,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener { ...@@ -98,10 +98,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
proxiedApps.add(item.packageName) proxiedApps.add(item.packageName)
check.setChecked(true) check.setChecked(true)
} }
if (!appsLoading.get) { if (!appsLoading.get) app.editor.putString(Key.individual, proxiedApps.mkString("\n")).apply()
profile.individual = proxiedApps.mkString("\n")
app.profileManager.updateProfile(profile)
}
} }
} }
...@@ -124,9 +121,9 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener { ...@@ -124,9 +121,9 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
private var loadingView: View = _ private var loadingView: View = _
private val appsLoading = new AtomicBoolean private val appsLoading = new AtomicBoolean
private var handler: Handler = _ private var handler: Handler = _
private val profile = app.currentProfile.orNull
private def initProxiedApps(str: String = profile.individual) = proxiedApps = str.split('\n').to[mutable.HashSet] private def initProxiedApps(str: String = app.settings.getString(Key.individual, null)) =
proxiedApps = str.split('\n').to[mutable.HashSet]
override def onDestroy() { override def onDestroy() {
instance = null instance = null
...@@ -143,7 +140,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener { ...@@ -143,7 +140,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
case R.id.action_apply_all => case R.id.action_apply_all =>
app.profileManager.getAllProfiles match { app.profileManager.getAllProfiles match {
case Some(profiles) => case Some(profiles) =>
val proxiedAppString = profile.individual val proxiedAppString = app.settings.getString(Key.individual, null)
profiles.foreach(p => { profiles.foreach(p => {
p.individual = proxiedAppString p.individual = proxiedAppString
app.profileManager.updateProfile(p) app.profileManager.updateProfile(p)
...@@ -153,8 +150,8 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener { ...@@ -153,8 +150,8 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
} }
return true return true
case R.id.action_export => case R.id.action_export =>
val bypass = profile.bypass val bypass = app.settings.getBoolean(Key.bypass, false)
val proxiedAppString = profile.individual val proxiedAppString = app.settings.getString(Key.individual, null)
val clip = ClipData.newPlainText(Key.individual, bypass + "\n" + proxiedAppString) val clip = ClipData.newPlainText(Key.individual, bypass + "\n" + proxiedAppString)
clipboard.setPrimaryClip(clip) clipboard.setPrimaryClip(clip)
Toast.makeText(this, R.string.action_export_msg, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.action_export_msg, Toast.LENGTH_SHORT).show()
...@@ -170,8 +167,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener { ...@@ -170,8 +167,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
val (enabled, apps) = if (i < 0) (proxiedAppString, "") val (enabled, apps) = if (i < 0) (proxiedAppString, "")
else (proxiedAppString.substring(0, i), proxiedAppString.substring(i + 1)) else (proxiedAppString.substring(0, i), proxiedAppString.substring(i + 1))
bypassSwitch.setChecked(enabled.toBoolean) bypassSwitch.setChecked(enabled.toBoolean)
profile.individual = apps app.editor.putString(Key.individual, apps).apply()
app.profileManager.updateProfile(profile)
Toast.makeText(this, R.string.action_import_msg, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.action_import_msg, Toast.LENGTH_SHORT).show()
appListView.setVisibility(View.GONE) appListView.setVisibility(View.GONE)
loadingView.setVisibility(View.VISIBLE) loadingView.setVisibility(View.VISIBLE)
...@@ -194,8 +190,6 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener { ...@@ -194,8 +190,6 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
protected override def onCreate(savedInstanceState: Bundle) { protected override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (profile == null) finish()
handler = new Handler() handler = new Handler()
this.setContentView(R.layout.layout_apps) this.setContentView(R.layout.layout_apps)
...@@ -211,23 +205,16 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener { ...@@ -211,23 +205,16 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
toolbar.inflateMenu(R.menu.app_manager_menu) toolbar.inflateMenu(R.menu.app_manager_menu)
toolbar.setOnMenuItemClickListener(this) toolbar.setOnMenuItemClickListener(this)
if (!profile.proxyApps) { if (!app.settings.getBoolean(Key.proxyApps, false)) app.editor.putBoolean(Key.proxyApps, true).apply()
profile.proxyApps = true
app.profileManager.updateProfile(profile)
}
findViewById(R.id.onSwitch).asInstanceOf[Switch] findViewById(R.id.onSwitch).asInstanceOf[Switch]
.setOnCheckedChangeListener((_, checked) => { .setOnCheckedChangeListener((_, checked) => {
profile.proxyApps = checked app.editor.putBoolean(Key.proxyApps, checked).apply()
app.profileManager.updateProfile(profile)
finish() finish()
}) })
bypassSwitch = findViewById(R.id.bypassSwitch).asInstanceOf[Switch] bypassSwitch = findViewById(R.id.bypassSwitch).asInstanceOf[Switch]
bypassSwitch.setChecked(profile.bypass) bypassSwitch.setChecked(app.settings.getBoolean(Key.bypass, false))
bypassSwitch.setOnCheckedChangeListener((_, checked) => { bypassSwitch.setOnCheckedChangeListener((_, checked) => app.editor.putBoolean(Key.bypass, checked).apply())
profile.bypass = checked
app.profileManager.updateProfile(profile)
})
initProxiedApps() initProxiedApps()
loadingView = findViewById(R.id.loading) loadingView = findViewById(R.id.loading)
......
package com.github.shadowsocks
import android.app.ProgressDialog
import android.os.{Bundle, Handler, Message}
import android.support.design.widget.Snackbar
import android.support.v14.preference.SwitchPreference
import be.mygod.preference.PreferenceFragment
import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.utils.{Key, TcpFastOpen, Utils}
class GlobalConfigFragment extends PreferenceFragment {
private var progressDialog: ProgressDialog = _
override def onCreatePreferences(bundle: Bundle, key: String) {
addPreferencesFromResource(R.xml.pref_global)
val switch = findPreference(Key.isAutoConnect).asInstanceOf[SwitchPreference]
switch.setOnPreferenceChangeListener((_, value) => {
BootReceiver.setEnabled(getActivity, value.asInstanceOf[Boolean])
true
})
if (getPreferenceManager.getSharedPreferences.getBoolean(Key.isAutoConnect, false)) {
BootReceiver.setEnabled(getActivity, true)
getPreferenceManager.getSharedPreferences.edit.remove(Key.isAutoConnect).apply()
}
switch.setChecked(BootReceiver.getEnabled(getActivity))
val tfo = findPreference(Key.tfo).asInstanceOf[SwitchPreference]
tfo.setChecked(TcpFastOpen.sendEnabled)
tfo.setOnPreferenceChangeListener((_, v) => {
val value = v.asInstanceOf[Boolean]
val result = TcpFastOpen.enabled(value)
if (result != null && result != "Success.")
Snackbar.make(getActivity.findViewById(android.R.id.content), result, Snackbar.LENGTH_LONG).show()
value == TcpFastOpen.sendEnabled
})
if (!TcpFastOpen.supported) {
tfo.setEnabled(false)
tfo.setSummary(getString(R.string.tcp_fastopen_summary_unsupported, java.lang.System.getProperty("os.version")))
}
findPreference("recovery").setOnPreferenceClickListener(_ => {
app.track("GlobalConfigFragment", "reset")
Utils.stopSsService(getActivity)
val h = showProgress(R.string.recovering)
Utils.ThrowableFuture {
app.copyAssets()
h.sendEmptyMessage(0)
}
true
})
}
private def showProgress(msg: Int): Handler = {
clearDialog()
progressDialog = ProgressDialog.show(getActivity, "", getString(msg), true, false)
new Handler {
override def handleMessage(msg: Message) {
clearDialog()
}
}
}
def clearDialog() {
if (progressDialog != null && progressDialog.isShowing) {
if (!getActivity.isDestroyed) progressDialog.dismiss()
progressDialog = null
}
}
}
package com.github.shadowsocks
import android.os.Bundle
import android.view.{LayoutInflater, View, ViewGroup}
class GlobalSettingsFragment extends ToolbarFragment {
override def onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle): View =
inflater.inflate(R.layout.layout_global_settings, container, false)
override def onViewCreated(view: View, savedInstanceState: Bundle) {
super.onViewCreated(view, savedInstanceState)
toolbar.setTitle(R.string.settings)
}
}
package com.github.shadowsocks
import android.app.Activity
import android.os.Bundle
import android.support.v7.widget.Toolbar
import android.support.v7.widget.Toolbar.OnMenuItemClickListener
class ProfileConfigActivity extends Activity {
override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout_profile_config)
val toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar]
toolbar.setTitle("Profile config") // TODO
toolbar.setNavigationIcon(R.drawable.ic_navigation_close)
toolbar.setNavigationOnClickListener(_ => finish())
toolbar.inflateMenu(R.menu.profile_config_menu)
toolbar.setOnMenuItemClickListener(getFragmentManager.findFragmentById(R.id.content)
.asInstanceOf[OnMenuItemClickListener])
}
}
...@@ -18,68 +18,86 @@ ...@@ -18,68 +18,86 @@
/* */ /* */
/*******************************************************************************/ /*******************************************************************************/
package com.github.shadowsocks.widget package com.github.shadowsocks
import android.animation.ValueAnimator import android.content._
import android.content.Context import android.os._
import android.support.design.widget.CoordinatorLayout import android.support.v14.preference.SwitchPreference
import android.support.design.widget.CoordinatorLayout.Behavior import android.support.v4.content.LocalBroadcastManager
import android.support.design.widget.Snackbar.SnackbarLayout import android.support.v7.app.AlertDialog
import android.support.v4.view.animation.FastOutSlowInInterpolator import android.support.v7.preference.Preference
import android.util.AttributeSet import android.support.v7.widget.Toolbar.OnMenuItemClickListener
import android.view.View import android.view.MenuItem
import com.github.clans.fab.FloatingActionMenu import be.mygod.preference._
import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.preferences.KcpCliPreferenceDialogFragment
import com.github.shadowsocks.utils.{Action, Key, Utils}
import scala.collection.JavaConverters._ class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListener {
private lazy val clipboard = getActivity.getSystemService(Context.CLIPBOARD_SERVICE).asInstanceOf[ClipboardManager]
private var profile: Profile = _
private var isProxyApps: SwitchPreference = _
/** override def onCreatePreferences(bundle: Bundle, key: String) {
* Behavior for com.github.clans.fab.FloatingActionMenu that is aware of Snackbars and scrolling. app.profileManager.getProfile(getActivity.getIntent.getIntExtra(Action.EXTRA_PROFILE_ID, -1)) match {
* case Some(p) =>
* @author Mygod profile = p
*/ profile.serialize(app.editor).apply()
class FloatingActionMenuBehavior(context: Context, attrs: AttributeSet) case None => getActivity.finish()
extends Behavior[FloatingActionMenu](context, attrs) { }
private var fabTranslationYAnimator: ValueAnimator = _ addPreferencesFromResource(R.xml.pref_profile)
private var fabTranslationY: Float = _ if (Build.VERSION.SDK_INT >= 25 && getActivity.getSystemService(classOf[UserManager]).isDemoUser) {
findPreference(Key.host).setSummary("shadowsocks.example.org")
findPreference(Key.remotePort).setSummary("1337")
findPreference(Key.password).setSummary("\u2022" * 32)
}
isProxyApps = findPreference(Key.proxyApps).asInstanceOf[SwitchPreference]
isProxyApps.setEnabled(Utils.isLollipopOrAbove || app.isNatEnabled)
isProxyApps.setOnPreferenceClickListener(_ => {
startActivity(new Intent(getActivity, classOf[AppManager]))
isProxyApps.setChecked(true)
false
})
}
override def layoutDependsOn(parent: CoordinatorLayout, child: FloatingActionMenu, dependency: View): Boolean = override def onResume() {
dependency.isInstanceOf[SnackbarLayout] super.onResume()
isProxyApps.setChecked(app.settings.getBoolean(Key.proxyApps, false)) // fetch proxyApps updated by AppManager
}
override def onDependentViewChanged(parent: CoordinatorLayout, child: FloatingActionMenu, override def onDisplayPreferenceDialog(preference: Preference): Unit = preference.getKey match {
dependency: View): Boolean = { case Key.kcpcli => displayPreferenceDialog(Key.kcpcli, new KcpCliPreferenceDialogFragment())
dependency match { case _ => super.onDisplayPreferenceDialog(preference)
case _: SnackbarLayout =>
var targetTransY = parent.getDependencies(child).asScala
.filter(view => view.isInstanceOf[SnackbarLayout] && parent.doViewsOverlap(child, view))
.map(view => view.getTranslationY - view.getHeight).reduceOption(_ min _).getOrElse(0F)
if (targetTransY > 0) targetTransY = 0
if (fabTranslationY != targetTransY) {
val currentTransY = child.getTranslationY
if (fabTranslationYAnimator != null && fabTranslationYAnimator.isRunning) fabTranslationYAnimator.cancel()
if (child.isShown && Math.abs(currentTransY - targetTransY) > child.getHeight * 0.667F) {
if (fabTranslationYAnimator == null) {
fabTranslationYAnimator = new ValueAnimator
fabTranslationYAnimator.setInterpolator(new FastOutSlowInInterpolator)
fabTranslationYAnimator.addUpdateListener(animation =>
child.setTranslationY(animation.getAnimatedValue.asInstanceOf[Float]))
}
fabTranslationYAnimator.setFloatValues(currentTransY, targetTransY)
fabTranslationYAnimator.start()
} else child.setTranslationY(targetTransY)
fabTranslationY = targetTransY
}
}
false
} }
override def onStartNestedScroll(parent: CoordinatorLayout, child: FloatingActionMenu, directTargetChild: View, override def onMenuItemClick(item: MenuItem): Boolean = item.getItemId match {
target: View, nestedScrollAxes: Int) = true case R.id.action_qr_code_nfc =>
override def onNestedScroll(parent: CoordinatorLayout, child: FloatingActionMenu, target: View, dxConsumed: Int, getFragmentManager.beginTransaction().add(new QRCodeDialog(profile.toString), "").commitAllowingStateLoss()
dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int) { true
super.onNestedScroll(parent, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed) case R.id.action_export =>
val dy = dyConsumed + dyUnconsumed clipboard.setPrimaryClip(ClipData.newPlainText(null, profile.toString))
if (child.isMenuButtonHidden) { true
if (dy < 0) child.showMenuButton(true) case R.id.action_delete =>
} else if (dy > 0) child.hideMenuButton(true) new AlertDialog.Builder(getActivity)
.setTitle("Confirm?") // TODO
.setPositiveButton(android.R.string.yes, ((_, _) => {
app.profileManager.delProfile(profile.id)
LocalBroadcastManager.getInstance(getActivity)
.sendBroadcast(new Intent(Action.PROFILE_REMOVED).putExtra(Action.EXTRA_PROFILE_ID, profile.id))
getActivity.finish()
}): DialogInterface.OnClickListener)
.setNegativeButton(android.R.string.no, null)
.create()
.show()
true
case R.id.action_apply =>
profile.deserialize(app.settings)
app.profileManager.updateProfile(profile)
LocalBroadcastManager.getInstance(getActivity)
.sendBroadcast(new Intent(Action.PROFILE_CHANGED).putExtra(Action.EXTRA_PROFILE_ID, profile.id))
getActivity.finish()
true
case _ => false
} }
} }
package com.github.shadowsocks
import java.nio.charset.Charset
import android.app.Activity
import android.nfc.{NdefMessage, NdefRecord, NfcAdapter}
import android.os.Bundle
import android.view.{LayoutInflater, View, ViewGroup}
import android.widget.{ImageView, LinearLayout}
import com.github.shadowsocks.utils.Utils
import net.glxn.qrgen.android.QRCode
final class QRCodeDialog(url: String) extends DialogFragment {
private lazy val nfcShareItem = url.getBytes(Charset.forName("UTF-8"))
private var adapter: NfcAdapter = _
override def onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle): View = {
val image = new ImageView(getActivity)
image.setLayoutParams(new LinearLayout.LayoutParams(-1, -1))
val qrcode = QRCode.from(url)
.withSize(Utils.dpToPx(getActivity, 250), Utils.dpToPx(getActivity, 250))
.asInstanceOf[QRCode].bitmap()
image.setImageBitmap(qrcode)
image
}
override def onAttach(activity: Activity) {
superOnAttach(activity)
adapter = NfcAdapter.getDefaultAdapter(getActivity)
if (adapter != null) adapter.setNdefPushMessage(new NdefMessage(Array(
new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, nfcShareItem, Array[Byte](), nfcShareItem))), activity)
}
override def onDetach() {
if (adapter != null) {
adapter.setNdefPushMessage(null, getActivity)
adapter = null
}
super.onDetach()
}
}
...@@ -46,14 +46,14 @@ trait ServiceBoundContext extends Context with IBinder.DeathRecipient { ...@@ -46,14 +46,14 @@ trait ServiceBoundContext extends Context with IBinder.DeathRecipient {
} }
} }
protected def registerCallback(): Unit = if (bgService != null && callback != null && !callbackRegistered) try { def registerCallback(): Unit = if (bgService != null && callback != null && !callbackRegistered) try {
bgService.registerCallback(callback) bgService.registerCallback(callback)
callbackRegistered = true callbackRegistered = true
} catch { } catch {
case _: RemoteException => // Nothing case _: RemoteException => // Nothing
} }
protected def unregisterCallback() { def unregisterCallback() {
if (bgService != null && callback != null && callbackRegistered) try bgService.unregisterCallback(callback) catch { if (bgService != null && callback != null && callbackRegistered) try bgService.unregisterCallback(callback) catch {
case _: RemoteException => case _: RemoteException =>
} }
......
...@@ -57,7 +57,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str ...@@ -57,7 +57,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str
.setColor(ContextCompat.getColor(service, R.color.material_primary_500)) .setColor(ContextCompat.getColor(service, R.color.material_primary_500))
.setTicker(service.getString(R.string.forward_success)) .setTicker(service.getString(R.string.forward_success))
.setContentTitle(profileName) .setContentTitle(profileName)
.setContentIntent(PendingIntent.getActivity(service, 0, new Intent(service, classOf[Shadowsocks]) .setContentIntent(PendingIntent.getActivity(service, 0, new Intent(service, classOf[MainActivity])
.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), 0)) .setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), 0))
.setSmallIcon(R.drawable.ic_stat_shadowsocks) .setSmallIcon(R.drawable.ic_stat_shadowsocks)
builder.addAction(R.drawable.ic_navigation_close, builder.addAction(R.drawable.ic_navigation_close,
......
package com.github.shadowsocks
import android.app.Fragment
import android.os.Bundle
import android.support.v7.widget.Toolbar
import android.view.View
class ToolbarFragment extends Fragment {
var toolbar: Toolbar = _
override def onViewCreated(view: View, savedInstanceState: Bundle) {
super.onViewCreated(view, savedInstanceState)
toolbar = view.findViewById(R.id.toolbar).asInstanceOf[Toolbar]
val activity = getActivity.asInstanceOf[MainActivity]
activity.drawer.setToolbar(activity, toolbar, true)
}
}
...@@ -23,8 +23,10 @@ package com.github.shadowsocks.database ...@@ -23,8 +23,10 @@ package com.github.shadowsocks.database
import java.net.URLEncoder import java.net.URLEncoder
import java.util.Locale import java.util.Locale
import android.content.SharedPreferences
import android.os.Binder import android.os.Binder
import android.util.Base64 import android.util.Base64
import com.github.shadowsocks.utils.Key
import com.j256.ormlite.field.{DataType, DatabaseField} import com.j256.ormlite.field.{DataType, DatabaseField}
class Profile { class Profile {
...@@ -32,7 +34,7 @@ class Profile { ...@@ -32,7 +34,7 @@ class Profile {
var id: Int = 0 var id: Int = 0
@DatabaseField @DatabaseField
var name: String = "Untitled" var name: String = ""
@DatabaseField @DatabaseField
var host: String = "" var host: String = ""
...@@ -100,4 +102,41 @@ class Profile { ...@@ -100,4 +102,41 @@ class Profile {
'#' + URLEncoder.encode(name, "utf-8") '#' + URLEncoder.encode(name, "utf-8")
def isMethodUnsafe: Boolean = "table".equalsIgnoreCase(method) || "rc4".equalsIgnoreCase(method) def isMethodUnsafe: Boolean = "table".equalsIgnoreCase(method) || "rc4".equalsIgnoreCase(method)
def serialize(editor: SharedPreferences.Editor): SharedPreferences.Editor = editor
.putString(Key.name, name)
.putString(Key.host, host)
.putInt(Key.localPort, localPort)
.putInt(Key.remotePort, remotePort)
.putString(Key.password, password)
.putString(Key.route, route)
.putString(Key.remoteDns, remoteDns)
.putBoolean(Key.proxyApps, proxyApps)
.putBoolean(Key.bypass, bypass)
.putBoolean(Key.udpdns, udpdns)
.putBoolean(Key.auth, auth)
.putBoolean(Key.ipv6, ipv6)
.putString(Key.individual, individual)
.putBoolean(Key.kcp, kcp)
.putInt(Key.kcpPort, kcpPort)
.putString(Key.kcpcli, kcpcli)
def deserialize(pref: SharedPreferences) {
// It's assumed that default values are never used, so 0/false/null is always used even if that isn't the case
name = pref.getString(Key.name, null)
host = pref.getString(Key.host, null)
localPort = pref.getInt(Key.localPort, 0)
remotePort = pref.getInt(Key.remotePort, 0)
password = pref.getString(Key.password, null)
route = pref.getString(Key.route, null)
remoteDns = pref.getString(Key.remoteDns, null)
proxyApps = pref.getBoolean(Key.proxyApps, false)
bypass = pref.getBoolean(Key.bypass, false)
udpdns = pref.getBoolean(Key.udpdns, false)
auth = pref.getBoolean(Key.auth, false)
ipv6 = pref.getBoolean(Key.ipv6, false)
individual = pref.getString(Key.individual, null)
kcp = pref.getBoolean(Key.kcp, false)
kcpPort = pref.getInt(Key.kcpPort, 0)
kcpcli = pref.getString(Key.kcpcli, null)
}
} }
...@@ -151,8 +151,6 @@ object Key { ...@@ -151,8 +151,6 @@ object Key {
val localPort = "localPortNum" val localPort = "localPortNum"
val remoteDns = "remoteDns" val remoteDns = "remoteDns"
val profileTip = "profileTip"
val kcp = "kcp" val kcp = "kcp"
val kcpPort = "kcpPort" val kcpPort = "kcpPort"
val kcpcli = "kcpcli" val kcpcli = "kcpcli"
...@@ -170,9 +168,14 @@ object State { ...@@ -170,9 +168,14 @@ object State {
} }
object Action { object Action {
val SERVICE = "com.github.shadowsocks.SERVICE" final val SERVICE = "com.github.shadowsocks.SERVICE"
val CLOSE = "com.github.shadowsocks.CLOSE" final val CLOSE = "com.github.shadowsocks.CLOSE"
val QUICK_SWITCH = "com.github.shadowsocks.QUICK_SWITCH" final val CONNECTED = "com.github.shadowsocks.CONNECTED" // TODO
final val PROFILE_CHANGED = "com.github.shadowsocks.PROFILE_CHANGED"
final val PROFILE_REMOVED = "com.github.shadowsocks.PROFILE_REMOVED"
final val QUICK_SWITCH = "com.github.shadowsocks.QUICK_SWITCH"
final val EXTRA_PROFILE_ID = "com.github.shadowsocks.EXTRA_PROFILE_ID"
} }
object Route { object Route {
......
package com.github.shadowsocks.utils
import java.util
import android.content.Context
import android.graphics.Typeface
import android.util.Log
import com.github.shadowsocks.ShadowsocksApplication.app
object Typefaces {
def get(c: Context, assetPath: String): Typeface = {
cache synchronized {
if (!cache.containsKey(assetPath)) {
try {
cache.put(assetPath, Typeface.createFromAsset(c.getAssets, assetPath))
} catch {
case e: Exception =>
Log.e("Typefaces", "Could not get typeface '" + assetPath + "' because " + e.getMessage)
app.track(e)
return null
}
}
return cache.get(assetPath)
}
}
private final val cache = new util.Hashtable[String, Typeface]
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment