diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml index f352528d369b63c52908f76b9c1bf852e76e8fac..7a367dbaa8f3a99e2943ab9bc9ef03dca6c8c5c2 100644 --- a/briar-android/AndroidManifest.xml +++ b/briar-android/AndroidManifest.xml @@ -1,18 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" +<manifest package="org.briarproject" + xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="11" - android:versionName="0.11" > + android:versionName="0.11"> <uses-sdk + xmlns:tools="http://schemas.android.com/tools" android:minSdkVersion="9" - android:targetSdkVersion="22" - xmlns:tools="http://schemas.android.com/tools" + android:targetSdkVersion="22" tools:overrideLibrary="android.support.v14.preference" - /> + /> - <uses-feature android:name="android.hardware.bluetooth" /> + <uses-feature android:name="android.hardware.bluetooth"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> @@ -23,183 +24,160 @@ <!-- Since API 23, this is needed to add contacts via Bluetooth --> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- FIXME: Only needed for alpha and beta builds --> - <uses-permission android:name="android.permission.READ_LOGS" /> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_LOGS"/> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:name=".android.BriarApplication" - android:theme="@style/BriarTheme" + android:allowBackup="false" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:logo="@drawable/logo" - android:allowBackup="false" > + android:theme="@style/BriarTheme"> <service android:name=".android.BriarService" - android:exported="false" > + android:exported="false"> <intent-filter> - <action android:name="org.briarproject.android.BriarService" /> + <action android:name="org.briarproject.android.BriarService"/> </intent-filter> </service> <activity android:name=".android.CrashReportActivity" - android:label="@string/crash_report_title" - android:taskAffinity="org.briarproject.android.CrashHandler" android:excludeFromRecents="true" + android:exported="false" + android:label="@string/crash_report_title" android:launchMode="singleInstance" android:process=":briar_error_handler" - android:exported="false" > + android:taskAffinity="org.briarproject.android.CrashHandler"> <intent-filter> - <action android:name="org.briarproject.REPORT_CRASH" /> - <category android:name="android.intent.category.DEFAULT" /> + <action android:name="org.briarproject.REPORT_CRASH"/> + <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> - <activity - android:name=".android.DashboardActivity" - android:label="@string/app_name" > - </activity> <activity android:name=".android.ExpiredActivity" - android:label="@string/app_name" > + android:label="@string/app_name"> </activity> <activity android:name=".android.PasswordActivity" android:label="@string/app_name" - android:windowSoftInputMode="stateVisible" > - </activity> - <activity - android:name=".android.SettingsActivity" - android:label="@string/settings_title" - android:parentActivityName=".android.DashboardActivity" > - <meta-data - android:name="android.support.PARENT_ACTIVITY" - android:value=".android.DashboardActivity" - /> + android:windowSoftInputMode="stateVisible"> </activity> <activity android:name=".android.SetupActivity" - android:label="@string/setup_title" > + android:label="@string/setup_title"> </activity> <activity android:name=".android.SplashScreenActivity" - android:label="@string/app_name" > + android:label="@string/app_name"> <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name=".android.TestingActivity" - android:label="@string/app_name" > + android:label="@string/app_name"> </activity> + <activity - android:name=".android.contact.ContactListActivity" - android:label="@string/contact_list_title" - android:parentActivityName=".android.DashboardActivity" > - <meta-data - android:name="android.support.PARENT_ACTIVITY" - android:value=".android.DashboardActivity" + android:name=".android.NavDrawerActivity" + android:theme="@style/BriarThemeNoActionBar.Default" + android:launchMode="singleTop" /> - </activity> + <activity android:name=".android.contact.ConversationActivity" android:label="@string/app_name" - android:windowSoftInputMode="stateHidden" - android:parentActivityName=".android.contact.ContactListActivity" > + android:parentActivityName=".android.NavDrawerActivity" + android:windowSoftInputMode="stateHidden"> <meta-data android:name="android.support.PARENT_ACTIVITY" - android:value=".android.contact.ContactListActivity" - /> + android:value=".android.NavDrawerActivity" + /> </activity> <activity android:name=".android.forum.AvailableForumsActivity" android:label="@string/available_forums_title" - android:parentActivityName=".android.forum.ForumListActivity" > + android:parentActivityName=".android.NavDrawerActivity"> <meta-data android:name="android.support.PARENT_ACTIVITY" - android:value=".android.forum.ForumListActivity" - /> + android:value=".android.NavDrawerActivity" + /> </activity> <activity android:name=".android.forum.CreateForumActivity" android:label="@string/create_forum_title" - android:windowSoftInputMode="stateVisible" - android:parentActivityName=".android.forum.ForumListActivity" > + android:parentActivityName=".android.NavDrawerActivity" + android:windowSoftInputMode="stateVisible"> <meta-data android:name="android.support.PARENT_ACTIVITY" - android:value=".android.forum.ForumListActivity" - /> + android:value=".android.NavDrawerActivity" + /> </activity> <activity android:name=".android.forum.ForumActivity" android:label="@string/app_name" - android:parentActivityName=".android.forum.ForumListActivity" > - <meta-data - android:name="android.support.PARENT_ACTIVITY" - android:value=".android.forum.ForumListActivity" - /> - </activity> - <activity - android:name=".android.forum.ForumListActivity" - android:label="@string/forums_title" - android:parentActivityName=".android.DashboardActivity" > + android:parentActivityName=".android.NavDrawerActivity"> <meta-data android:name="android.support.PARENT_ACTIVITY" - android:value=".android.DashboardActivity" - /> + android:value=".android.NavDrawerActivity" + /> </activity> <activity android:name=".android.forum.ReadForumPostActivity" android:label="@string/app_name" - android:parentActivityName=".android.forum.ForumListActivity" > + android:parentActivityName=".android.NavDrawerActivity"> <meta-data android:name="android.support.PARENT_ACTIVITY" - android:value=".android.forum.ForumListActivity" - /> + android:value=".android.NavDrawerActivity" + /> </activity> <activity android:name=".android.forum.ShareForumActivity" android:label="@string/app_name" - android:parentActivityName=".android.forum.ForumListActivity" > + android:parentActivityName=".android.NavDrawerActivity"> <meta-data android:name="android.support.PARENT_ACTIVITY" - android:value=".android.forum.ForumListActivity" - /> + android:value=".android.NavDrawerActivity" + /> </activity> <activity android:name=".android.forum.WriteForumPostActivity" android:label="@string/app_name" - android:windowSoftInputMode="stateVisible" - android:parentActivityName=".android.forum.ForumListActivity" > + android:parentActivityName=".android.NavDrawerActivity" + android:windowSoftInputMode="stateVisible"> <meta-data android:name="android.support.PARENT_ACTIVITY" - android:value=".android.forum.ForumListActivity" - /> + android:value=".android.NavDrawerActivity" + /> </activity> <activity android:name=".android.identity.CreateIdentityActivity" android:label="@string/new_identity_title" - android:windowSoftInputMode="stateVisible" > + android:windowSoftInputMode="stateVisible"> </activity> <activity android:name=".android.invitation.AddContactActivity" android:label="@string/add_contact_title" - android:parentActivityName=".android.contact.ContactListActivity" > + android:parentActivityName=".android.NavDrawerActivity"> <meta-data android:name="android.support.PARENT_ACTIVITY" - android:value=".android.contact.ContactListActivity" - /> + android:value=".android.NavDrawerActivity" + /> </activity> <activity android:name=".android.StartupFailureActivity" - android:label="@string/startup_failed_activity_title" > + android:label="@string/startup_failed_activity_title"> </activity> <activity android:name=".android.panic.PanicPreferencesActivity" - android:label="@string/panic_setting" > + android:label="@string/panic_setting"> <intent-filter> - <action android:name="info.guardianproject.panic.action.CONNECT" /> - <action android:name="info.guardianproject.panic.action.DISCONNECT" /> - <category android:name="android.intent.category.DEFAULT" /> + <action android:name="info.guardianproject.panic.action.CONNECT"/> + <action android:name="info.guardianproject.panic.action.DISCONNECT"/> + + <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> <activity @@ -208,12 +186,12 @@ android:theme="@android:style/Theme.NoDisplay"> <!-- this can never have launchMode singleTask or singleInstance! --> <intent-filter> - <action android:name="info.guardianproject.panic.action.TRIGGER" /> - <category android:name="android.intent.category.DEFAULT" /> + <action android:name="info.guardianproject.panic.action.TRIGGER"/> + <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> <activity android:name=".android.panic.ExitActivity" - android:theme="@android:style/Theme.NoDisplay" /> + android:theme="@android:style/Theme.NoDisplay"/> </application> </manifest> diff --git a/briar-android/res/layout/activity_nav_drawer.xml b/briar-android/res/layout/activity_nav_drawer.xml new file mode 100644 index 0000000000000000000000000000000000000000..f7f90efa8e99a2be3cbf94dc1dc30995d20cc566 --- /dev/null +++ b/briar-android/res/layout/activity_nav_drawer.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="utf-8"?> +<android.support.v4.widget.DrawerLayout + android:id="@+id/drawer_layout" + 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"> + + <!-- The first child(root) is the content view --> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <android.support.v7.widget.Toolbar + android:id="@+id/toolbar" + style="@style/BriarToolbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/colorPrimary" + android:minHeight="?attr/actionBarSize" + /> + + <FrameLayout + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <FrameLayout + android:id="@+id/content_fragment" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/default_background"/> + + <RelativeLayout + android:id="@+id/container_progress" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + android:background="@color/default_background" + android:visibility="invisible" + tools:visibility="visible"> + + <ProgressBar + android:id="@+id/progress_bar" + style="?android:attr/progressBarStyleLargeInverse" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true"/> + + <TextView + android:id="@+id/title_progress_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/progress_bar" + android:gravity="center" + android:paddingTop="@dimen/margin_large" + tools:text="progress bar title" + /> + + </RelativeLayout> + + </FrameLayout> + </LinearLayout> + + <!-- The second child is the menu --> + <include + android:id="@+id/navigation_menu_drawer" + layout="@layout/navigation_menu" + android:layout_width="@dimen/nav_drawer_width" + android:layout_height="match_parent" + android:layout_gravity="start"/> + +</android.support.v4.widget.DrawerLayout> \ No newline at end of file diff --git a/briar-android/res/layout/fragment_dashboard.xml b/briar-android/res/layout/fragment_dashboard.xml new file mode 100644 index 0000000000000000000000000000000000000000..1d73431f59b46c0699d6cef6895bdd8d54bafff1 --- /dev/null +++ b/briar-android/res/layout/fragment_dashboard.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/dashboard_background" + > + + +</RelativeLayout> \ No newline at end of file diff --git a/briar-android/res/layout/navigation_menu.xml b/briar-android/res/layout/navigation_menu.xml new file mode 100644 index 0000000000000000000000000000000000000000..51360c6758ac9a6bc0ba5892b25dd43fccb0325d --- /dev/null +++ b/briar-android/res/layout/navigation_menu.xml @@ -0,0 +1,110 @@ +<?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="@dimen/nav_drawer_width" + android:layout_height="match_parent" + android:background="@color/menu_background" + android:orientation="vertical"> + + <TextView + android:id="@+id/nav_menu_header" + style="@style/BriarTextTitleInverted" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@color/briar_primary" + android:paddingBottom="@dimen/margin_large" + android:paddingLeft="@dimen/margin_large" + android:paddingTop="@dimen/margin_xlarge" + android:textStyle="bold" + tools:text="Username" + /> + + <ScrollView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fadingEdge="none" + android:fillViewport="true"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <android.support.v7.widget.AppCompatButton + android:id="@+id/nav_btn_contacts" + style="@style/NavMenuButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:drawableLeft="@drawable/social_person" + android:onClick="onNavigationClick" + android:text="@string/contact_list_button"/> + + <View + style="@style/Divider" + android:layout_width="match_parent" + android:layout_height="@dimen/nav_seperator_height" + android:layout_marginLeft="@dimen/margin_large"/> + + <android.support.v7.widget.AppCompatButton + android:id="@+id/nav_btn_forums" + style="@style/NavMenuButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:drawableLeft="@drawable/social_chat" + android:onClick="onNavigationClick" + android:text="@string/forums_button"/> + + <View + style="@style/Divider" + android:layout_width="match_parent" + android:layout_height="@dimen/margin_seperator" + android:layout_marginLeft="@dimen/margin_large"/> + + <android.support.v7.widget.AppCompatButton + android:id="@+id/nav_btn_settings" + style="@style/NavMenuButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:drawableLeft="@drawable/action_settings" + android:onClick="onNavigationClick" + android:text="@string/settings_button"/> + + <View + style="@style/Divider" + android:layout_width="match_parent" + android:layout_height="@dimen/margin_seperator" + android:layout_marginLeft="@dimen/margin_large"/> + + <android.support.v7.widget.AppCompatButton + android:id="@+id/nav_btn_signout" + style="@style/NavMenuButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:drawableLeft="@drawable/device_access_accounts" + android:onClick="onNavigationClick" + android:text="@string/sign_out_button"/> + + <View + style="@style/Divider" + android:layout_width="match_parent" + android:layout_height="@dimen/margin_seperator" + android:layout_marginLeft="@dimen/margin_large"/> + + <FrameLayout + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1.0"/> + + <include + layout="@layout/transports_list" + android:layout_width="match_parent" + android:layout_height="wrap_content" + /> + + </LinearLayout> + + </ScrollView> + + +</LinearLayout> \ No newline at end of file diff --git a/briar-android/res/values/color.xml b/briar-android/res/values/color.xml index fc0ea39777f88fcf3c7a538fa03c2afca4f3f34e..94f4ea9cbc52c6fefc3c61c9321bd6aa0eef3c98 100644 --- a/briar-android/res/values/color.xml +++ b/briar-android/res/values/color.xml @@ -26,4 +26,8 @@ <!-- this is needed as preference_category_material layout uses this color as the text color --> <color name="preference_fallback_accent_color">@color/briar_accent</color> + <color name="default_background">#ffffff</color> + <color name="default_seperator">#000000</color> + <color name="default_seperator_inverted">#ffffff</color> + <color name="menu_background">#FFFFFF</color> </resources> \ No newline at end of file diff --git a/briar-android/res/values/dimens.xml b/briar-android/res/values/dimens.xml index 35c316f1216ce179e2cc4f1c6120e04fda42d7b4..e417b0897aa47a6ad3824c2cfdd7e7c9bd74d19a 100644 --- a/briar-android/res/values/dimens.xml +++ b/briar-android/res/values/dimens.xml @@ -17,5 +17,7 @@ <dimen name="text_size_medium">16sp</dimen> <dimen name="text_size_large">20sp</dimen> <dimen name="text_size_xlarge">34sp</dimen> + <dimen name="nav_drawer_width">300dp</dimen> + <dimen name="nav_seperator_height">1dp</dimen> </resources> diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index 1ac6b50ebce552540c62051b8f8fc2b8f3be1886..70e2d20c5ce7c8204453dfdff75c52284c2872d8 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - - + <string name="nav_drawer_open_description">Open the navigation drawer</string> + <string name="nav_drawer_close_description">Close the navigation drawer</string> <string name="app_name">Briar</string> <string name="crash_report_title">Briar Crash Report</string> <string name="ongoing_notification_title">Signed into Briar</string> @@ -141,4 +141,15 @@ <string name="dialog_message_delete_contact">Are you sure that you want to remove this contact and all messages exchanged with this contact?</string> <string name="dialog_title_connect_panic_app">Confirm Panic App</string> <string name="dialog_message_connect_panic_app">Are you sure that you want to allow %1$s to trigger destructive panic button actions?</string> + <string name="dialog_title_welcome">Welcome to Briar</string> + <string name="dialog_welcome_message">Add a contact to start communicating securely or press the icon in the upper left corner of the screen for more options.</string> + <string name="dialog_button_ok">OK</string> + <!-- Toolbar headers --> + <string name="dashboard_toolbar_header">Briar</string> + <string name="settings_toolbar_header">Settings</string> + <string name="contacts_toolbar_header">Contacts</string> + <string name="forums_toolbar_header">Forums</string> + <!-- Progress titles --> + <string name="progress_title_logout">Signing out of Briar..</string> + <string name="progress_title_please_wait">Please wait..</string> </resources> diff --git a/briar-android/res/values/styles.xml b/briar-android/res/values/styles.xml index 7a869c9051cf36783ee71cde92ad5d8157f2bfa5..2d10abf414fc10b5ea803aab15049cb8908fafd3 100644 --- a/briar-android/res/values/styles.xml +++ b/briar-android/res/values/styles.xml @@ -12,6 +12,36 @@ <item name="android:textColorLink">@color/briar_text_link</item> </style> + <style name="BriarThemeNoActionBar" parent="Theme.AppCompat.Light.NoActionBar"> + <item name="toolbarStyle">@style/BriarToolbar</item> + <item name="colorPrimary">@color/briar_primary</item> + <item name="colorPrimaryDark">@color/briar_primary_dark</item> + <item name="colorAccent">@color/briar_primary</item> + <item name="android:textColorPrimary">@color/briar_text_primary</item> + <item name="android:textColorPrimaryInverse">@color/briar_text_primary_inverse</item> + <item name="android:textColorSecondary">@color/briar_text_primary</item> + <item name="android:textColorLink">@color/briar_text_link</item> + </style> + + <style name="BriarThemeNoActionBar.Default" /> + + <style name="BriarToolbar" parent="Widget.AppCompat.Toolbar"> + <item name="android:background">?colorPrimary</item> + <item name="android:textColorPrimary">@color/briar_text_primary_inverse</item> + <item name="android:textSize">@dimen/text_size_medium</item> + <item name="colorPrimary">@color/briar_primary</item> + <item name="titleTextAppearance">@style/BriarToolbarTextAppearance</item> + <item name="android:theme">@style/BriarToolbarTheme</item> + </style> + + <style name="BriarToolbarTheme"> + <item name="colorControlNormal">@color/briar_text_primary_inverse</item> + </style> + + <style name="BriarToolbarTextAppearance" parent="TextAppearance.Widget.AppCompat.Toolbar.Title"> + <item name="android:textColor">@color/briar_text_primary_inverse</item> + </style> + <style name="BriarTheme" parent="BriarBaseTheme"> <item name="preferenceTheme">@style/PreferenceThemeOverlay</item> <item name="android:listSeparatorTextViewStyle">@style/BriarTheme.ListSeparatorTextView</item> @@ -33,6 +63,10 @@ <item name="android:textColor">@android:color/primary_text_light</item> </style> + <style name="BriarTextTitleInverted" parent="BriarTextTitle"> + <item name="android:textColor">@android:color/primary_text_dark</item> + </style> + <style name="BriarTextBody"> <item name="android:textSize">@dimen/text_size_small</item> <item name="android:textColor">@android:color/primary_text_light</item> @@ -47,6 +81,17 @@ <item name="android:layout_height">1px</item> </style> + <style name="NavMenuButton" parent="Widget.AppCompat.Button.Borderless.Colored"> + <item name="android:textSize">@dimen/text_size_medium</item> + <item name="android:textColor">@android:color/tertiary_text_light</item> + <item name="android:paddingTop">@dimen/margin_large</item> + <item name="android:paddingBottom">@dimen/margin_large</item> + <item name="android:drawablePadding">@dimen/margin_xlarge</item> + <item name="android:gravity">left|center_vertical</item> + <item name="android:layout_margin">0dp</item> + <item name="android:paddingLeft">@dimen/margin_large</item> + </style> + <!-- This fixes a UI bug in the support preference library --> <style name="BriarTheme.ListSeparatorTextView"> <item name="android:textSize">14sp</item> diff --git a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java index f83f83082e1327a2750c827aa89efaaaef7b573f..a355f6b7375f59055fbb989ca989dc9d5e2cbf17 100644 --- a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java +++ b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java @@ -9,10 +9,8 @@ import android.support.v4.app.NotificationCompat; import android.support.v4.app.TaskStackBuilder; import org.briarproject.R; -import org.briarproject.android.contact.ContactListActivity; import org.briarproject.android.contact.ConversationActivity; import org.briarproject.android.forum.ForumActivity; -import org.briarproject.android.forum.ForumListActivity; import org.briarproject.api.Settings; import org.briarproject.api.android.AndroidExecutor; import org.briarproject.api.android.AndroidNotificationManager; @@ -205,10 +203,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, t.addNextIntent(i); b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); } else { - Intent i = new Intent(appContext, ContactListActivity.class); + Intent i = new Intent(appContext, NavDrawerActivity.class); + i.putExtra(NavDrawerActivity.INTENT_CONTACTS, true); i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP); TaskStackBuilder t = TaskStackBuilder.create(appContext); - t.addParentStack(ContactListActivity.class); + t.addParentStack(NavDrawerActivity.class); t.addNextIntent(i); b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); } @@ -283,10 +282,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, t.addNextIntent(i); b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); } else { - Intent i = new Intent(appContext, ForumListActivity.class); + Intent i = new Intent(appContext, NavDrawerActivity.class); + i.putExtra(NavDrawerActivity.INTENT_FORUMS, true); i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP); TaskStackBuilder t = TaskStackBuilder.create(appContext); - t.addParentStack(ForumListActivity.class); + t.addParentStack(NavDrawerActivity.class); t.addNextIntent(i); b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); } diff --git a/briar-android/src/org/briarproject/android/BriarActivity.java b/briar-android/src/org/briarproject/android/BriarActivity.java index fcc4ab604db3cb354b3c386d5e4b1e71d1148873..8d88cc5db1748b7a6f85254362eff717096c18e4 100644 --- a/briar-android/src/org/briarproject/android/BriarActivity.java +++ b/briar-android/src/org/briarproject/android/BriarActivity.java @@ -14,6 +14,7 @@ import org.briarproject.api.db.DatabaseExecutor; import org.briarproject.api.lifecycle.LifecycleManager; import java.util.concurrent.Executor; +import java.util.logging.Level; import java.util.logging.Logger; import javax.inject.Inject; @@ -22,7 +23,10 @@ import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION; import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; @SuppressLint("Registered") -public class BriarActivity extends BaseActivity { +public abstract class BriarActivity extends BaseActivity { + + public static final String KEY_LOCAL_AUTHOR_HANDLE = "briar.LOCAL_AUTHOR_HANDLE"; + public static final String KEY_STARTUP_FAILED = "briar.STARTUP_FAILED"; public static final int REQUEST_PASSWORD = 1; @@ -125,7 +129,7 @@ public class BriarActivity extends BaseActivity { }); } - protected void runOnDbThread(final Runnable task) { + public void runOnDbThread(final Runnable task) { dbExecutor.execute(new Runnable() { public void run() { try { diff --git a/briar-android/src/org/briarproject/android/BriarFragmentActivity.java b/briar-android/src/org/briarproject/android/BriarFragmentActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..573866aff27d51f124de60f3d7b332681019211a --- /dev/null +++ b/briar-android/src/org/briarproject/android/BriarFragmentActivity.java @@ -0,0 +1,102 @@ +package org.briarproject.android; + +import android.app.AlertDialog; +import android.support.annotation.AnimRes; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.ActionBar; + +import org.briarproject.R; +import org.briarproject.android.fragment.BaseFragment; +import org.briarproject.android.contact.ContactListFragment; +import org.briarproject.android.fragment.DashboardFragment; +import org.briarproject.android.forum.ForumListFragment; +import org.briarproject.android.fragment.SettingsFragment; + +/** + * This class should be extended by classes that wish to utilise fragments in + * Briar, it encapsulates all fragment related code. + */ +public abstract class BriarFragmentActivity extends BriarActivity { + + private void updateToolbarTitle(String fragmentTag) { + ActionBar actionBar = getSupportActionBar(); + if (actionBar == null) + return; + + if (fragmentTag.equals(DashboardFragment.TAG)) { + actionBar.setTitle(R.string.dashboard_toolbar_header); + } else if (fragmentTag.equals(SettingsFragment.TAG)) { + actionBar.setTitle(R.string.settings_toolbar_header); + } else if (fragmentTag.equals(ContactListFragment.TAG)) { + actionBar.setTitle(R.string.contacts_toolbar_header); + } else if (fragmentTag.equals(ForumListFragment.TAG)) { + actionBar.setTitle(R.string.forums_toolbar_header); + } + } + + protected void clearBackStack() { + getSupportFragmentManager() + .popBackStackImmediate( + null, + FragmentManager.POP_BACK_STACK_INCLUSIVE + ); + } + + @Override + public void onBackPressed() { + if (getSupportFragmentManager().getBackStackEntryCount() == 0 && + getSupportFragmentManager() + .findFragmentByTag(ContactListFragment.TAG) == null) { + /* + This Makes sure that the first fragment (ContactListFragment) the + user sees is the same as the last fragment the user sees before + exiting. This models the typical Google navigation behaviour such + as in Gmail/Inbox. + */ + startFragment(ContactListFragment.newInstance()); + + } else { + super.onBackPressed(); + } + } + + protected void startFragment(BaseFragment fragment) { + if (getSupportFragmentManager().getBackStackEntryCount() == 0) + this.startFragment(fragment, false); + else + this.startFragment(fragment, true); + } + + protected void showMessageDialog(int titleStringId, int msgStringId) { + // TODO replace with custom dialog fragment ? + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(titleStringId); + builder.setMessage(msgStringId); + builder.setPositiveButton(R.string.dialog_button_ok, null); + AlertDialog dialog = builder.create(); + dialog.show(); + } + + protected void startFragment(BaseFragment fragment, + boolean isAddedToBackStack) { + this.startFragment(fragment, 0, 0, isAddedToBackStack); + } + + protected void startFragment(BaseFragment fragment, + @AnimRes int inAnimation, @AnimRes int outAnimation, + boolean isAddedToBackStack) { + FragmentTransaction trans = + getSupportFragmentManager().beginTransaction(); + if (inAnimation != 0 && outAnimation != 0) { + trans.setCustomAnimations(inAnimation, 0, 0, outAnimation); + } + trans.replace(R.id.content_fragment, fragment, fragment.getUniqueTag()); + if (isAddedToBackStack) { + trans.addToBackStack(fragment.getUniqueTag()); + } + trans.commit(); + updateToolbarTitle(fragment.getUniqueTag()); + } + +} diff --git a/briar-android/src/org/briarproject/android/BriarService.java b/briar-android/src/org/briarproject/android/BriarService.java index 90f774272b27d472ed7ad096141f3d3ce2a86d5b..f050d7c11c79901385cbc67139327c0af2f7e12d 100644 --- a/briar-android/src/org/briarproject/android/BriarService.java +++ b/briar-android/src/org/briarproject/android/BriarService.java @@ -70,7 +70,7 @@ public class BriarService extends RoboService { b.setContentText(getText(R.string.ongoing_notification_text)); b.setWhen(0); // Don't show the time b.setOngoing(true); - Intent i = new Intent(this, DashboardActivity.class); + Intent i = new Intent(this, NavDrawerActivity.class); i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP); b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0)); @@ -117,7 +117,7 @@ public class BriarService extends RoboService { NotificationManager nm = (NotificationManager) o; nm.notify(FAILURE_NOTIFICATION_ID, b.build()); // Bring the dashboard to the front to clear the back stack - i = new Intent(BriarService.this, DashboardActivity.class); + i = new Intent(BriarService.this, NavDrawerActivity.class); i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP); i.putExtra("briar.STARTUP_FAILED", true); startActivity(i); diff --git a/briar-android/src/org/briarproject/android/DashboardActivity.java b/briar-android/src/org/briarproject/android/DashboardActivity.java deleted file mode 100644 index 06e44b9b9d060f2a3647161b816d6c8b2362006e..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/DashboardActivity.java +++ /dev/null @@ -1,381 +0,0 @@ -package org.briarproject.android; - -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.Button; -import android.widget.GridView; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.ProgressBar; -import android.widget.TextView; - -import org.briarproject.R; -import org.briarproject.android.contact.ContactListActivity; -import org.briarproject.android.forum.ForumListActivity; -import org.briarproject.android.util.LayoutUtils; -import org.briarproject.api.TransportId; -import org.briarproject.api.android.ReferenceManager; -import org.briarproject.api.db.DbException; -import org.briarproject.api.event.Event; -import org.briarproject.api.event.EventBus; -import org.briarproject.api.event.EventListener; -import org.briarproject.api.event.TransportDisabledEvent; -import org.briarproject.api.event.TransportEnabledEvent; -import org.briarproject.api.identity.IdentityManager; -import org.briarproject.api.identity.LocalAuthor; -import org.briarproject.api.plugins.Plugin; -import org.briarproject.api.plugins.PluginManager; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; - -import javax.inject.Inject; - -import static android.view.Gravity.CENTER; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import static java.util.logging.Level.INFO; -import static java.util.logging.Level.WARNING; -import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH; - -public class DashboardActivity extends BriarActivity implements EventListener { - - private static final Logger LOG = - Logger.getLogger(DashboardActivity.class.getName()); - - private List<Transport> transports; - private BaseAdapter transportsAdapter; - - @Inject private ReferenceManager referenceManager; - @Inject private PluginManager pluginManager; - - // Fields that are accessed from background threads must be volatile - @Inject private volatile IdentityManager identityManager; - @Inject private volatile EventBus eventBus; - - @Override - public void onCreate(Bundle state) { - super.onCreate(state); - handleIntent(getIntent()); - } - - @Override - public void onResume() { - super.onResume(); - updateTransports(); - eventBus.addListener(this); - } - - @Override - public void onPause() { - super.onPause(); - eventBus.removeListener(this); - } - - @Override - public void onNewIntent(Intent i) { - super.onNewIntent(i); - handleIntent(i); - } - - @Override - public void eventOccurred(Event e) { - if (e instanceof TransportEnabledEvent) { - TransportId id = ((TransportEnabledEvent) e).getTransportId(); - if (LOG.isLoggable(INFO)) { - LOG.info("TransportEnabledEvent: " + id.getString()); - } - setTransport(id, true); - } else if (e instanceof TransportDisabledEvent) { - TransportId id = ((TransportDisabledEvent) e).getTransportId(); - if (LOG.isLoggable(INFO)) { - LOG.info("TransportDisabledEvent: " + id.getString()); - } - setTransport(id, false); - } - } - - private void handleIntent(Intent i) { - boolean failed = i.getBooleanExtra("briar.STARTUP_FAILED", false); - long handle = i.getLongExtra("briar.LOCAL_AUTHOR_HANDLE", -1); - if (failed) { - finish(); - LOG.info("Exiting"); - System.exit(0); - } else if (handle == -1) { - // The activity has been launched before - showButtons(); - } else { - // The activity was launched from the setup wizard - LocalAuthor a = referenceManager.removeReference(handle, - LocalAuthor.class); - // The reference may be null if the activity has been recreated, - // for example due to screen rotation - if (a == null) { - showButtons(); - } else { - showSpinner(); - storeLocalAuthor(a); - } - } - } - - private void showButtons() { - ListView.LayoutParams matchMatch = - new ListView.LayoutParams(MATCH_PARENT, MATCH_PARENT); - final List<Button> buttons = new ArrayList<Button>(); - - Button contactsButton = new Button(this); - contactsButton.setLayoutParams(matchMatch); - contactsButton.setBackgroundResource(0); - contactsButton.setCompoundDrawablesWithIntrinsicBounds(0, - R.drawable.social_person, 0, 0); - contactsButton.setText(R.string.contact_list_button); - contactsButton.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - startActivity(new Intent(DashboardActivity.this, - ContactListActivity.class)); - } - }); - buttons.add(contactsButton); - - Button forumsButton = new Button(this); - forumsButton.setLayoutParams(matchMatch); - forumsButton.setBackgroundResource(0); - forumsButton.setCompoundDrawablesWithIntrinsicBounds(0, - R.drawable.social_chat, 0, 0); - forumsButton.setText(R.string.forums_button); - forumsButton.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - startActivity(new Intent(DashboardActivity.this, - ForumListActivity.class)); - } - }); - buttons.add(forumsButton); - - Button settingsButton = new Button(this); - settingsButton.setLayoutParams(matchMatch); - settingsButton.setBackgroundResource(0); - settingsButton.setCompoundDrawablesWithIntrinsicBounds(0, - R.drawable.action_settings, 0, 0); - settingsButton.setText(R.string.settings_button); - settingsButton.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - startActivity(new Intent(DashboardActivity.this, - SettingsActivity.class)); - } - }); - buttons.add(settingsButton); - - Button signOutButton = new Button(this); - signOutButton.setLayoutParams(matchMatch); - signOutButton.setBackgroundResource(0); - signOutButton.setCompoundDrawablesWithIntrinsicBounds(0, - R.drawable.device_access_accounts, 0, 0); - signOutButton.setText(R.string.sign_out_button); - signOutButton.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - showSpinner(); - signOut(); - } - }); - buttons.add(signOutButton); - - int pad = LayoutUtils.getPadding(this); - - LinearLayout layout = new LinearLayout(this); - layout.setLayoutParams(MATCH_MATCH); - layout.setOrientation(LinearLayout.VERTICAL); - - GridView grid = new GridView(this); - LinearLayout.LayoutParams params = - new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 1f); - grid.setLayoutParams(params); - grid.setGravity(CENTER); - grid.setPadding(pad, pad, pad, pad); - Resources res = getResources(); - grid.setBackgroundColor(res.getColor(R.color.dashboard_background)); - grid.setNumColumns(2); - grid.setAdapter(new BaseAdapter() { - - public int getCount() { - return buttons.size(); - } - - public Object getItem(int position) { - return buttons.get(position); - } - - public long getItemId(int position) { - return 0; - } - - public View getView(int position, View convertView, - ViewGroup parent) { - return buttons.get(position); - } - }); - layout.addView(grid); - - // inflate transports layout - LayoutInflater inflater = (LayoutInflater) getSystemService - (Context.LAYOUT_INFLATER_SERVICE); - ViewGroup transportsLayout = (ViewGroup) inflater. - inflate(R.layout.transports_list, layout); - - initializeTransports(); - - GridView transportsView = (GridView) transportsLayout.findViewById( - R.id.transportsView); - transportsView.setAdapter(transportsAdapter); - - setContentView(layout); - } - - private void showSpinner() { - LinearLayout layout = new LinearLayout(this); - layout.setLayoutParams(MATCH_MATCH); - layout.setGravity(CENTER); - - ProgressBar progress = new ProgressBar(this); - progress.setIndeterminate(true); - layout.addView(progress); - - setContentView(layout); - } - - private void storeLocalAuthor(final LocalAuthor a) { - runOnDbThread(new Runnable() { - public void run() { - try { - long now = System.currentTimeMillis(); - identityManager.addLocalAuthor(a); - long duration = System.currentTimeMillis() - now; - if (LOG.isLoggable(INFO)) - LOG.info("Storing author took " + duration + " ms"); - runOnUiThread(new Runnable() { - public void run() { - showButtons(); - } - }); - } catch (DbException e) { - if (LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - } - } - }); - } - - private void initializeTransports() { - transports = new ArrayList<Transport>(3); - - Transport tor = new Transport(); - tor.id = new TransportId("tor"); - Plugin torPlugin = pluginManager.getPlugin(tor.id); - tor.enabled = torPlugin != null && torPlugin.isRunning(); - tor.iconId = R.drawable.transport_tor; - tor.textId = R.string.transport_tor; - transports.add(tor); - - Transport bt = new Transport(); - bt.id = new TransportId("bt"); - Plugin btPlugin = pluginManager.getPlugin(bt.id); - bt.enabled = btPlugin != null && btPlugin.isRunning(); - bt.iconId = R.drawable.transport_bt; - bt.textId = R.string.transport_bt; - transports.add(bt); - - Transport lan = new Transport(); - lan.id = new TransportId("lan"); - Plugin lanPlugin = pluginManager.getPlugin(lan.id); - lan.enabled = lanPlugin != null && lanPlugin.isRunning(); - lan.iconId = R.drawable.transport_lan; - lan.textId = R.string.transport_lan; - transports.add(lan); - - transportsAdapter = new BaseAdapter() { - @Override - public int getCount() { - return transports.size(); - } - - @Override - public Transport getItem(int position) { - return transports.get(position); - } - - @Override - public long getItemId(int position) { - return 0; - } - - @Override - public View getView(int position, View convertView, - ViewGroup parent) { - LayoutInflater inflater = (LayoutInflater) getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - ViewGroup view = (ViewGroup) inflater - .inflate(R.layout.list_item_transport, parent, false); - - Transport t = getItem(position); - Resources r = getResources(); - - int c; - if (t.enabled) { - c = r.getColor(R.color.briar_green_light); - } else { - c = r.getColor(android.R.color.tertiary_text_light); - } - - ImageView icon = (ImageView) view.findViewById(R.id.imageView); - icon.setImageDrawable(r.getDrawable(t.iconId)); - icon.setColorFilter(c); - - TextView text = (TextView) view.findViewById(R.id.textView); - text.setText(getString(t.textId)); - - return view; - } - }; - } - - private void setTransport(final TransportId id, final boolean enabled) { - runOnUiThread(new Runnable() { - public void run() { - if (transports == null || transportsAdapter == null) return; - for (Transport t : transports) { - if (t.id.equals(id)) { - t.enabled = enabled; - transportsAdapter.notifyDataSetChanged(); - break; - } - } - } - }); - } - - private void updateTransports() { - if (transports == null || transportsAdapter == null) return; - for (Transport t : transports) { - Plugin plugin = pluginManager.getPlugin(t.id); - t.enabled = plugin != null && plugin.isRunning(); - } - transportsAdapter.notifyDataSetChanged(); - } - - private static class Transport { - TransportId id; - boolean enabled; - int iconId; - int textId; - } -} diff --git a/briar-android/src/org/briarproject/android/NavDrawerActivity.java b/briar-android/src/org/briarproject/android/NavDrawerActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..f56f06a6dcdf4fa72dbac24da90b86f32d9b0c4c --- /dev/null +++ b/briar-android/src/org/briarproject/android/NavDrawerActivity.java @@ -0,0 +1,407 @@ +package org.briarproject.android; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.Bundle; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.widget.AppCompatButton; +import android.support.v7.widget.Toolbar; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.GridView; +import android.widget.ImageView; +import android.widget.TextView; + +import org.briarproject.R; +import org.briarproject.android.fragment.BaseFragment; +import org.briarproject.android.contact.ContactListFragment; +import org.briarproject.android.forum.ForumListFragment; +import org.briarproject.android.fragment.SettingsFragment; +import org.briarproject.android.util.CustomAnimations; +import org.briarproject.api.TransportId; +import org.briarproject.api.android.ReferenceManager; +import org.briarproject.api.db.DbException; +import org.briarproject.api.event.Event; +import org.briarproject.api.event.EventBus; +import org.briarproject.api.event.EventListener; +import org.briarproject.api.event.TransportDisabledEvent; +import org.briarproject.api.event.TransportEnabledEvent; +import org.briarproject.api.identity.IdentityManager; +import org.briarproject.api.identity.LocalAuthor; +import org.briarproject.api.plugins.Plugin; +import org.briarproject.api.plugins.PluginManager; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import javax.inject.Inject; + +import roboguice.RoboGuice; +import roboguice.inject.InjectView; + +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; + +public class NavDrawerActivity extends BriarFragmentActivity implements + BaseFragment.BaseFragmentListener, EventListener { + + public static final String INTENT_CONTACTS = "intent_contacts"; + public static final String INTENT_FORUMS = "intent_forums"; + + private static final Logger LOG = + Logger.getLogger(NavDrawerActivity.class.getName()); + + private final static String PREFS_USER_SETTINGS = "prefs_user_settings"; + private final static String KEY_SEEN_WELCOME_MESSAGE = "welcome_message"; + + private ActionBarDrawerToggle drawerToggle; + + @Inject + private ReferenceManager referenceManager; + // Fields that are accessed from background threads must be volatile + @Inject + private volatile IdentityManager identityManager; + @Inject + private PluginManager pluginManager; + @Inject + protected volatile EventBus eventBus; + + @InjectView(R.id.toolbar) + private Toolbar toolbar; + @InjectView(R.id.drawer_layout) + private DrawerLayout drawerLayout; + @InjectView(R.id.nav_btn_contacts) + private AppCompatButton contactButton; + @InjectView(R.id.nav_btn_contacts) + private AppCompatButton forumsButton; + @InjectView(R.id.nav_btn_contacts) + private AppCompatButton settingsButton; + @InjectView(R.id.nav_menu_header) + private TextView menuHeader; + @InjectView(R.id.title_progress_bar) + private TextView progressTitle; + @InjectView(R.id.container_progress) + ViewGroup progressViewGroup; + @InjectView(R.id.transportsView) + private GridView transportsView; + + private List<Transport> transports; + private BaseAdapter transportsAdapter; + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + if (!isStartupFailed(intent)) { + checkAuthorHandle(intent); + clearBackStack(); + if (intent.getBooleanExtra(INTENT_FORUMS, false)) + startFragment(ForumListFragment.newInstance()); + else if (intent.getBooleanExtra(INTENT_CONTACTS, false)) + startFragment(ContactListFragment.newInstance()); + } + } + + @SuppressWarnings("ConstantConditions") + @Override + public void onCreate(Bundle state) { + super.onCreate(state); + + if (isStartupFailed(getIntent())) + return; + + // TODO inflate and inject with @ContentView with RoboGuice 3.0 and later + setContentView(R.layout.activity_nav_drawer); + RoboGuice.getInjector(this).injectViewMembers(this); + + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + + drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, + R.string.nav_drawer_open_description, + R.string.nav_drawer_close_description + ) { + + public void onDrawerClosed(View view) { + super.onDrawerClosed(view); + } + + public void onDrawerOpened(View drawerView) { + super.onDrawerOpened(drawerView); + } + }; + drawerLayout.setDrawerListener(drawerToggle); + startFragment(ContactListFragment.newInstance()); + checkAuthorHandle(getIntent()); + + initializeTransports(getLayoutInflater()); + transportsView.setAdapter(transportsAdapter); + + welcomeMessageCheck(); + } + + private void welcomeMessageCheck() { + SharedPreferences prefs = getSharedPreferences(PREFS_USER_SETTINGS, + Context.MODE_PRIVATE); + if (!prefs.getBoolean(KEY_SEEN_WELCOME_MESSAGE, false)) { + showMessageDialog(R.string.dialog_title_welcome, + R.string.dialog_welcome_message); + prefs.edit().putBoolean(KEY_SEEN_WELCOME_MESSAGE, true).apply(); + } + } + + @Override + public void onResume() { + super.onResume(); + eventBus.addListener(this); + updateTransports(); + } + + @Override + protected void onPause() { + super.onPause(); + eventBus.removeListener(this); + } + + private void checkAuthorHandle(Intent intent) { + long handle = intent.getLongExtra(KEY_LOCAL_AUTHOR_HANDLE, -1); + if (handle != -1) { + // The activity was launched from the setup wizard + LocalAuthor a = referenceManager.removeReference(handle, + LocalAuthor.class); + if (a != null) { + showLoadingScreen(true, R.string.progress_title_please_wait); + storeLocalAuthor(a); + } + } + } + + private boolean isStartupFailed(Intent intent) { + if (intent.getBooleanExtra(KEY_STARTUP_FAILED, false)) { + finish(); + LOG.info("Exiting"); + System.exit(0); + return true; + } + return false; + } + + private void storeLocalAuthor(final LocalAuthor a) { + runOnDbThread(new Runnable() { + public void run() { + try { + long now = System.currentTimeMillis(); + identityManager.addLocalAuthor(a); + long duration = System.currentTimeMillis() - now; + if (LOG.isLoggable(INFO)) + LOG.info("Storing author took " + duration + " ms"); + + runOnUiThread(new Runnable() { + public void run() { + hideLoadingScreen(); + } + }); + + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } + } + }); + } + + public void onNavigationClick(View view) { + drawerLayout.closeDrawer(GravityCompat.START); + clearBackStack(); + switch (view.getId()) { + case R.id.nav_btn_contacts: + startFragment(ContactListFragment.newInstance()); + break; + case R.id.nav_btn_forums: + startFragment(ForumListFragment.newInstance()); + break; + case R.id.nav_btn_settings: + startFragment(SettingsFragment.newInstance()); + break; + case R.id.nav_btn_signout: + signOut(); + break; + } + } + + + @Override + public void onBackPressed() { + if (getSupportFragmentManager().getBackStackEntryCount() == 0 + && drawerLayout.isDrawerOpen(GravityCompat.START)) { + drawerLayout.closeDrawer(GravityCompat.START); + return; + } + super.onBackPressed(); + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + drawerToggle.syncState(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + drawerToggle.onConfigurationChanged(newConfig); + } + + @Override + protected void signOut() { + showLoadingScreen(true, R.string.progress_title_logout); + super.signOut(); + } + + @Override + public void showLoadingScreen(boolean isBlocking, int stringId) { + if (isBlocking) { + // Disable navigation drawer slide to open + drawerLayout + .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); + CustomAnimations.animateHeight(toolbar, false, 250); + } + progressTitle.setText(stringId); + progressViewGroup.setVisibility(View.VISIBLE); + } + + @Override + public void hideLoadingScreen() { + drawerLayout + .setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); + CustomAnimations.animateHeight(toolbar, true, 250); + progressViewGroup.setVisibility(View.INVISIBLE); + } + + private void initializeTransports(final LayoutInflater inflater) { + transports = new ArrayList<Transport>(3); + + Transport tor = new Transport(); + tor.id = new TransportId("tor"); + Plugin torPlugin = pluginManager.getPlugin(tor.id); + tor.enabled = torPlugin != null && torPlugin.isRunning(); + tor.iconId = R.drawable.transport_tor; + tor.textId = R.string.transport_tor; + transports.add(tor); + + Transport bt = new Transport(); + bt.id = new TransportId("bt"); + Plugin btPlugin = pluginManager.getPlugin(bt.id); + bt.enabled = btPlugin != null && btPlugin.isRunning(); + bt.iconId = R.drawable.transport_bt; + bt.textId = R.string.transport_bt; + transports.add(bt); + + Transport lan = new Transport(); + lan.id = new TransportId("lan"); + Plugin lanPlugin = pluginManager.getPlugin(lan.id); + lan.enabled = lanPlugin != null && lanPlugin.isRunning(); + lan.iconId = R.drawable.transport_lan; + lan.textId = R.string.transport_lan; + transports.add(lan); + + transportsAdapter = new BaseAdapter() { + @Override + public int getCount() { + return transports.size(); + } + + @Override + public Transport getItem(int position) { + return transports.get(position); + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, + ViewGroup parent) { + ViewGroup view = (ViewGroup) inflater + .inflate(R.layout.list_item_transport, parent, false); + + Transport t = getItem(position); + Resources r = getResources(); + + int c; + if (t.enabled) { + c = r.getColor(R.color.briar_green_light); + } else { + c = r.getColor(android.R.color.tertiary_text_light); + } + + ImageView icon = (ImageView) view.findViewById(R.id.imageView); + icon.setImageDrawable(r.getDrawable(t.iconId)); + icon.setColorFilter(c); + + TextView text = (TextView) view.findViewById(R.id.textView); + text.setText(getString(t.textId)); + + return view; + } + }; + } + + private void setTransport(final TransportId id, final boolean enabled) { + runOnUiThread(new Runnable() { + public void run() { + if (transports == null || transportsAdapter == null) return; + for (Transport t : transports) { + if (t.id.equals(id)) { + t.enabled = enabled; + transportsAdapter.notifyDataSetChanged(); + break; + } + } + } + }); + } + + private void updateTransports() { + if (transports == null || transportsAdapter == null) return; + for (Transport t : transports) { + Plugin plugin = pluginManager.getPlugin(t.id); + t.enabled = plugin != null && plugin.isRunning(); + } + transportsAdapter.notifyDataSetChanged(); + } + + @Override + public void eventOccurred(Event e) { + if (e instanceof TransportEnabledEvent) { + TransportId id = ((TransportEnabledEvent) e).getTransportId(); + if (LOG.isLoggable(INFO)) { + LOG.info("TransportEnabledEvent: " + id.getString()); + } + setTransport(id, true); + } else if (e instanceof TransportDisabledEvent) { + TransportId id = ((TransportDisabledEvent) e).getTransportId(); + if (LOG.isLoggable(INFO)) { + LOG.info("TransportDisabledEvent: " + id.getString()); + } + setTransport(id, false); + } + } + + private static class Transport { + TransportId id; + boolean enabled; + int iconId; + int textId; + } +} diff --git a/briar-android/src/org/briarproject/android/SetupActivity.java b/briar-android/src/org/briarproject/android/SetupActivity.java index 237dca1f8f5fc9b059838718caa56c6469b314b2..81e832d5f0da4e64b9c41fbfed3613885a894d54 100644 --- a/briar-android/src/org/briarproject/android/SetupActivity.java +++ b/briar-android/src/org/briarproject/android/SetupActivity.java @@ -176,8 +176,8 @@ public class SetupActivity extends BaseActivity implements OnClickListener, runOnUiThread(new Runnable() { public void run() { Intent i = new Intent(SetupActivity.this, - DashboardActivity.class); - i.putExtra("briar.LOCAL_AUTHOR_HANDLE", handle); + NavDrawerActivity.class); + i.putExtra(BriarActivity.KEY_LOCAL_AUTHOR_HANDLE, handle); i.setFlags(FLAG_ACTIVITY_NEW_TASK); startActivity(i); finish(); diff --git a/briar-android/src/org/briarproject/android/SplashScreenActivity.java b/briar-android/src/org/briarproject/android/SplashScreenActivity.java index ddf3dbc23fc2f6457703f0d2231118b5c11c3a3b..f35aea013901a85b15f59dbc53a1b6dcd78ce7d9 100644 --- a/briar-android/src/org/briarproject/android/SplashScreenActivity.java +++ b/briar-android/src/org/briarproject/android/SplashScreenActivity.java @@ -84,7 +84,7 @@ public class SplashScreenActivity extends RoboSplashActivity { Injector i = RoboGuice.getBaseApplicationInjector(getApplication()); DatabaseConfig databaseConfig = i.getInstance(DatabaseConfig.class); if (hex != null && databaseConfig.databaseExists()) { - startActivity(new Intent(this, DashboardActivity.class)); + startActivity(new Intent(this, NavDrawerActivity.class)); } else { prefs.edit().clear().apply(); FileUtils.deleteFileOrDir( diff --git a/briar-android/src/org/briarproject/android/TestingConstants.java b/briar-android/src/org/briarproject/android/TestingConstants.java index aa8dce68f7387ca2723907058590c18aee18873a..7c20594f6315e159e633ad5ec6400d6979d97fd7 100644 --- a/briar-android/src/org/briarproject/android/TestingConstants.java +++ b/briar-android/src/org/briarproject/android/TestingConstants.java @@ -5,7 +5,7 @@ import static java.util.logging.Level.OFF; import java.util.logging.Level; -interface TestingConstants { +public interface TestingConstants { /** * Whether this is an alpha or beta build. This should be set to false for @@ -25,7 +25,7 @@ interface TestingConstants { boolean PREVENT_SCREENSHOTS = TESTING ? false : true; /** - * Whether to allow TestingActivity to be launched from SettingsActivity. + * Whether to allow TestingActivity to be launched from SettingsFragment. */ boolean SHOW_TESTING_ACTIVITY = TESTING ? true : false; diff --git a/briar-android/src/org/briarproject/android/contact/ContactListActivity.java b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java similarity index 77% rename from briar-android/src/org/briarproject/android/contact/ContactListActivity.java rename to briar-android/src/org/briarproject/android/contact/ContactListFragment.java index 03e7595a7e1d82a5139cd6442b58cafae7ec2f04..d2bff01e582b45a40cb0c2bee5adbc1c897c9db8 100644 --- a/briar-android/src/org/briarproject/android/contact/ContactListActivity.java +++ b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java @@ -2,12 +2,15 @@ package org.briarproject.android.contact; import android.content.Intent; import android.os.Bundle; +import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; import android.support.v7.widget.LinearLayoutManager; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import org.briarproject.R; -import org.briarproject.android.BriarActivity; +import org.briarproject.android.fragment.BaseEventFragment; import org.briarproject.android.invitation.AddContactActivity; import org.briarproject.android.util.BriarRecyclerView; import org.briarproject.api.contact.Contact; @@ -21,7 +24,6 @@ import org.briarproject.api.event.ContactDisconnectedEvent; import org.briarproject.api.event.ContactRemovedEvent; import org.briarproject.api.event.Event; import org.briarproject.api.event.EventBus; -import org.briarproject.api.event.EventListener; import org.briarproject.api.event.MessageValidatedEvent; import org.briarproject.api.messaging.MessagingManager; import org.briarproject.api.messaging.PrivateMessageHeader; @@ -39,63 +41,82 @@ import javax.inject.Inject; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; -public class ContactListActivity extends BriarActivity - implements EventListener { +public class ContactListFragment extends BaseEventFragment { private static final Logger LOG = - Logger.getLogger(ContactListActivity.class.getName()); + Logger.getLogger(ContactListFragment.class.getName()); - @Inject private ConnectionRegistry connectionRegistry; - private ContactListAdapter adapter = null; - private BriarRecyclerView list = null; + public final static String TAG = "ContactListFragment"; - // Fields that are accessed from background threads must be volatile - @Inject private volatile ContactManager contactManager; - @Inject private volatile MessagingManager messagingManager; - @Inject private volatile EventBus eventBus; + public static ContactListFragment newInstance() { + + Bundle args = new Bundle(); + + ContactListFragment fragment = new ContactListFragment(); + fragment.setArguments(args); + return fragment; + } @Override - public void onCreate(Bundle state) { - super.onCreate(state); + public String getUniqueTag() { + return TAG; + } - setContentView(R.layout.activity_contact_list); + @Inject + private ConnectionRegistry connectionRegistry; + private ContactListAdapter adapter = null; + private BriarRecyclerView list = null; - adapter = new ContactListAdapter(this); - list = (BriarRecyclerView) findViewById(R.id.contactList); - list.setLayoutManager(new LinearLayoutManager(this)); + // Fields that are accessed from background threads must be volatile + @Inject + private volatile ContactManager contactManager; + @Inject + private volatile MessagingManager messagingManager; + @Inject + private volatile EventBus eventBus; + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View contentView = + inflater.inflate(R.layout.activity_contact_list, container, + false); + + adapter = new ContactListAdapter(getContext()); + list = (BriarRecyclerView) contentView.findViewById(R.id.contactList); + list.setLayoutManager(new LinearLayoutManager(getContext())); list.setAdapter(adapter); list.setEmptyText(getString(R.string.no_contacts)); // Show a floating action button - FloatingActionButton fab = (FloatingActionButton) findViewById( - R.id.addContactFAB); + FloatingActionButton fab = + (FloatingActionButton) contentView.findViewById( + R.id.addContactFAB); // handle FAB click fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - startActivity(new Intent(ContactListActivity.this, + startActivity(new Intent(getContext(), AddContactActivity.class)); } }); - } - @Override - public void onPause() { - super.onPause(); - eventBus.removeListener(this); + return contentView; } + @Override public void onResume() { super.onResume(); - eventBus.addListener(this); + loadContacts(); } private void loadContacts() { - runOnDbThread(new Runnable() { + listener.runOnDbThread(new Runnable() { public void run() { try { long now = System.currentTimeMillis(); @@ -113,7 +134,7 @@ public class ContactListActivity extends BriarActivity contacts.add(new ContactListItem(c, connected, groupId, headers)); } catch (NoSuchContactException e) { - LOG.info("Contact removed"); + // Continue } } displayContacts(contacts); @@ -129,7 +150,7 @@ public class ContactListActivity extends BriarActivity } private void displayContacts(final List<ContactListItem> contacts) { - runOnUiThread(new Runnable() { + listener.runOnUiThread(new Runnable() { public void run() { adapter.clear(); if (contacts.size() == 0) list.showData(); @@ -160,7 +181,7 @@ public class ContactListActivity extends BriarActivity } private void reloadConversation(final GroupId g) { - runOnDbThread(new Runnable() { + listener.runOnDbThread(new Runnable() { public void run() { try { long now = System.currentTimeMillis(); @@ -183,7 +204,7 @@ public class ContactListActivity extends BriarActivity private void updateItem(final ContactId c, final Collection<PrivateMessageHeader> headers) { - runOnUiThread(new Runnable() { + listener.runOnUiThread(new Runnable() { public void run() { int position = adapter.findItemPosition(c); ContactListItem item = adapter.getItem(position); @@ -196,7 +217,7 @@ public class ContactListActivity extends BriarActivity } private void removeItem(final ContactId c) { - runOnUiThread(new Runnable() { + listener.runOnUiThread(new Runnable() { public void run() { int position = adapter.findItemPosition(c); ContactListItem item = adapter.getItem(position); @@ -206,7 +227,7 @@ public class ContactListActivity extends BriarActivity } private void setConnected(final ContactId c, final boolean connected) { - runOnUiThread(new Runnable() { + listener.runOnUiThread(new Runnable() { public void run() { int position = adapter.findItemPosition(c); ContactListItem item = adapter.getItem(position); diff --git a/briar-android/src/org/briarproject/android/forum/ForumListAdapter.java b/briar-android/src/org/briarproject/android/forum/ForumListAdapter.java index 17a7e2f2a4f847574c19e2dcb97c9582498f5477..0fb713a74d443b45c2a85f0461e8449ef65022c2 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumListAdapter.java +++ b/briar-android/src/org/briarproject/android/forum/ForumListAdapter.java @@ -18,11 +18,11 @@ import static android.text.TextUtils.TruncateAt.END; import static android.widget.LinearLayout.HORIZONTAL; import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP_1; -class ForumListAdapter extends ArrayAdapter<ForumListItem> { +public class ForumListAdapter extends ArrayAdapter<ForumListItem> { private final int pad; - ForumListAdapter(Context ctx) { + public ForumListAdapter(Context ctx) { super(ctx, android.R.layout.simple_expandable_list_item_1, new ArrayList<ForumListItem>()); pad = LayoutUtils.getPadding(ctx); diff --git a/briar-android/src/org/briarproject/android/forum/ForumListActivity.java b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java similarity index 83% rename from briar-android/src/org/briarproject/android/forum/ForumListActivity.java rename to briar-android/src/org/briarproject/android/forum/ForumListFragment.java index fef329409a2b21c4c62bb4a0f2ba9d64fcf91c5c..55f34f75693021528fa1027914f591f6907489c6 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumListActivity.java +++ b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java @@ -3,15 +3,15 @@ package org.briarproject.android.forum; import android.content.Intent; import android.content.res.Resources; import android.os.Bundle; +import android.support.annotation.Nullable; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnCreateContextMenuListener; +import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; -import android.widget.AdapterView.OnItemClickListener; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ListView; @@ -19,15 +19,13 @@ import android.widget.TextView; import android.widget.Toast; import org.briarproject.R; -import org.briarproject.android.BriarActivity; +import org.briarproject.android.fragment.BaseEventFragment; import org.briarproject.android.util.HorizontalBorder; import org.briarproject.android.util.LayoutUtils; import org.briarproject.android.util.ListLoadingProgressBar; import org.briarproject.api.db.DbException; import org.briarproject.api.db.NoSuchSubscriptionException; import org.briarproject.api.event.Event; -import org.briarproject.api.event.EventBus; -import org.briarproject.api.event.EventListener; import org.briarproject.api.event.MessageValidatedEvent; import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent; import org.briarproject.api.event.SubscriptionAddedEvent; @@ -58,13 +56,24 @@ import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH; import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP; import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP_1; -public class ForumListActivity extends BriarActivity - implements EventListener, OnClickListener, OnItemClickListener, - OnCreateContextMenuListener { +public class ForumListFragment extends BaseEventFragment implements + AdapterView.OnItemClickListener, View.OnClickListener { + + public final static String TAG = "ForumListFragment"; - private static final int MENU_ITEM_UNSUBSCRIBE = 1; private static final Logger LOG = - Logger.getLogger(ForumListActivity.class.getName()); + Logger.getLogger(ForumListFragment.class.getName()); + + public static ForumListFragment newInstance() { + + Bundle args = new Bundle(); + + ForumListFragment fragment = new ForumListFragment(); + fragment.setArguments(args); + return fragment; + } + + private static final int MENU_ITEM_UNSUBSCRIBE = 1; private TextView empty = null; private ForumListAdapter adapter = null; @@ -74,20 +83,21 @@ public class ForumListActivity extends BriarActivity private ImageButton newForumButton = null; // Fields that are accessed from background threads must be volatile - @Inject private volatile ForumManager forumManager; - @Inject private volatile EventBus eventBus; + @Inject + private volatile ForumManager forumManager; + @Nullable @Override - public void onCreate(Bundle state) { - super.onCreate(state); - LinearLayout layout = new LinearLayout(this); + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + LinearLayout layout = new LinearLayout(getContext()); layout.setLayoutParams(MATCH_MATCH); layout.setOrientation(VERTICAL); layout.setGravity(CENTER_HORIZONTAL); - int pad = LayoutUtils.getPadding(this); + int pad = LayoutUtils.getPadding(getContext()); - empty = new TextView(this); + empty = new TextView(getContext()); empty.setLayoutParams(MATCH_WRAP_1); empty.setGravity(CENTER); empty.setTextSize(18); @@ -95,8 +105,8 @@ public class ForumListActivity extends BriarActivity empty.setVisibility(GONE); layout.addView(empty); - adapter = new ForumListAdapter(this); - list = new ListView(this); + adapter = new ForumListAdapter(getContext()); + list = new ListView(getContext()); list.setLayoutParams(MATCH_WRAP_1); list.setAdapter(adapter); list.setOnItemClickListener(this); @@ -105,10 +115,10 @@ public class ForumListActivity extends BriarActivity layout.addView(list); // Show a progress bar while the list is loading - loading = new ListLoadingProgressBar(this); + loading = new ListLoadingProgressBar(getContext()); layout.addView(loading); - available = new TextView(this); + available = new TextView(getContext()); available.setLayoutParams(MATCH_WRAP); available.setGravity(CENTER); available.setTextSize(18); @@ -120,33 +130,37 @@ public class ForumListActivity extends BriarActivity available.setVisibility(GONE); layout.addView(available); - layout.addView(new HorizontalBorder(this)); + layout.addView(new HorizontalBorder(getContext())); - LinearLayout footer = new LinearLayout(this); + LinearLayout footer = new LinearLayout(getContext()); footer.setLayoutParams(MATCH_WRAP); footer.setOrientation(HORIZONTAL); footer.setGravity(CENTER); footer.setBackgroundColor(res.getColor(R.color.button_bar_background)); - newForumButton = new ImageButton(this); + newForumButton = new ImageButton(getContext()); newForumButton.setBackgroundResource(0); newForumButton.setImageResource(R.drawable.social_new_chat); newForumButton.setOnClickListener(this); footer.addView(newForumButton); layout.addView(footer); - setContentView(layout); + return layout; + } + + @Override + public String getUniqueTag() { + return TAG; } @Override public void onResume() { super.onResume(); - eventBus.addListener(this); loadHeaders(); } private void loadHeaders() { clearHeaders(); - runOnDbThread(new Runnable() { + listener.runOnDbThread(new Runnable() { public void run() { try { long now = System.currentTimeMillis(); @@ -173,7 +187,7 @@ public class ForumListActivity extends BriarActivity } private void clearHeaders() { - runOnUiThread(new Runnable() { + listener.runOnUiThread(new Runnable() { public void run() { empty.setVisibility(GONE); list.setVisibility(GONE); @@ -186,7 +200,7 @@ public class ForumListActivity extends BriarActivity private void displayHeaders(final Forum f, final Collection<ForumPostHeader> headers) { - runOnUiThread(new Runnable() { + listener.runOnUiThread(new Runnable() { public void run() { list.setVisibility(VISIBLE); loading.setVisibility(GONE); @@ -202,7 +216,7 @@ public class ForumListActivity extends BriarActivity } private void displayAvailable(final int availableCount) { - runOnUiThread(new Runnable() { + listener.runOnUiThread(new Runnable() { public void run() { if (adapter.isEmpty()) empty.setVisibility(VISIBLE); loading.setVisibility(GONE); @@ -239,12 +253,6 @@ public class ForumListActivity extends BriarActivity else list.setSelection(firstUnread); } - @Override - public void onPause() { - super.onPause(); - eventBus.removeListener(this); - } - public void eventOccurred(Event e) { if (e instanceof MessageValidatedEvent) { MessageValidatedEvent m = (MessageValidatedEvent) e; @@ -269,7 +277,7 @@ public class ForumListActivity extends BriarActivity } private void loadHeaders(final GroupId g) { - runOnDbThread(new Runnable() { + listener.runOnDbThread(new Runnable() { public void run() { try { long now = System.currentTimeMillis(); @@ -291,7 +299,7 @@ public class ForumListActivity extends BriarActivity } private void removeForum(final GroupId g) { - runOnUiThread(new Runnable() { + listener.runOnUiThread(new Runnable() { public void run() { ForumListItem item = findForum(g); if (item != null) { @@ -308,7 +316,7 @@ public class ForumListActivity extends BriarActivity } private void loadAvailable() { - runOnDbThread(new Runnable() { + listener.runOnDbThread(new Runnable() { public void run() { try { long now = System.currentTimeMillis(); @@ -327,15 +335,16 @@ public class ForumListActivity extends BriarActivity public void onClick(View view) { if (view == available) { - startActivity(new Intent(this, AvailableForumsActivity.class)); + startActivity(new Intent(getContext(), + AvailableForumsActivity.class)); } else if (view == newForumButton) { - startActivity(new Intent(this, CreateForumActivity.class)); + startActivity(new Intent(getContext(), CreateForumActivity.class)); } } public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - Intent i = new Intent(this, ForumActivity.class); + Intent i = new Intent(getContext(), ForumActivity.class); Forum f = adapter.getItem(position).getForum(); i.putExtra("briar.GROUP_ID", f.getId().getBytes()); i.putExtra("briar.FORUM_NAME", f.getName()); @@ -344,7 +353,7 @@ public class ForumListActivity extends BriarActivity @Override public void onCreateContextMenu(ContextMenu menu, View view, - ContextMenu.ContextMenuInfo info) { + ContextMenuInfo info) { String delete = getString(R.string.unsubscribe); menu.add(NONE, MENU_ITEM_UNSUBSCRIBE, NONE, delete); } @@ -357,13 +366,13 @@ public class ForumListActivity extends BriarActivity ForumListItem item = adapter.getItem(position); removeSubscription(item.getForum()); String unsubscribed = getString(R.string.unsubscribed_toast); - Toast.makeText(this, unsubscribed, LENGTH_SHORT).show(); + Toast.makeText(getContext(), unsubscribed, LENGTH_SHORT).show(); } return true; } private void removeSubscription(final Forum f) { - runOnDbThread(new Runnable() { + listener.runOnDbThread(new Runnable() { public void run() { try { long now = System.currentTimeMillis(); @@ -378,4 +387,4 @@ public class ForumListActivity extends BriarActivity } }); } -} \ No newline at end of file +} diff --git a/briar-android/src/org/briarproject/android/forum/ForumListItemComparator.java b/briar-android/src/org/briarproject/android/forum/ForumListItemComparator.java index 2c7ac144933d001922b2809dd7bc7875752cdb3a..bc50231c30d85a47d1baf68794a4832a3829dd11 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumListItemComparator.java +++ b/briar-android/src/org/briarproject/android/forum/ForumListItemComparator.java @@ -2,9 +2,9 @@ package org.briarproject.android.forum; import java.util.Comparator; -class ForumListItemComparator implements Comparator<ForumListItem> { +public class ForumListItemComparator implements Comparator<ForumListItem> { - static final ForumListItemComparator INSTANCE = + public static final ForumListItemComparator INSTANCE = new ForumListItemComparator(); public int compare(ForumListItem a, ForumListItem b) { diff --git a/briar-android/src/org/briarproject/android/fragment/BaseEventFragment.java b/briar-android/src/org/briarproject/android/fragment/BaseEventFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..f353b6e08dddc6c4862fc21a531b0b591f14dcf1 --- /dev/null +++ b/briar-android/src/org/briarproject/android/fragment/BaseEventFragment.java @@ -0,0 +1,28 @@ +package org.briarproject.android.fragment; + +import org.briarproject.api.event.EventBus; +import org.briarproject.api.event.EventListener; + +import javax.inject.Inject; + +/** + * Created by Ernir Erlingsson (ernir@ymirmobile.com) on 8.1.2016. + */ +public abstract class BaseEventFragment extends BaseFragment implements + EventListener { + + @Inject + protected volatile EventBus eventBus; + + @Override + public void onResume() { + super.onResume(); + eventBus.addListener(this); + } + + @Override + public void onPause() { + super.onPause(); + eventBus.removeListener(this); + } +} diff --git a/briar-android/src/org/briarproject/android/fragment/BaseFragment.java b/briar-android/src/org/briarproject/android/fragment/BaseFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..5d69a5870edf9d45e7cc0986085f1101250f568a --- /dev/null +++ b/briar-android/src/org/briarproject/android/fragment/BaseFragment.java @@ -0,0 +1,35 @@ +package org.briarproject.android.fragment; + +import android.content.Context; + +import org.briarproject.android.BriarActivity; + +import roboguice.fragment.RoboFragment; + +public abstract class BaseFragment extends RoboFragment { + + public abstract String getUniqueTag(); + + protected BaseFragmentListener listener; + + protected BriarActivity briarActivity; + + @Override + public void onAttach(Context context) { + super.onAttach(context); + try { + listener = (BaseFragmentListener) context; + } catch (ClassCastException e) { + throw new ClassCastException( + "Using class must implement BaseFragmentListener"); + } + } + + public interface BaseFragmentListener { + void showLoadingScreen(boolean isBlocking, int stringId); + void hideLoadingScreen(); + void runOnUiThread(Runnable runnable); + void runOnDbThread(Runnable runnable); + } + +} diff --git a/briar-android/src/org/briarproject/android/fragment/DashboardFragment.java b/briar-android/src/org/briarproject/android/fragment/DashboardFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..b1eb92585bb10a5d7ef1135dae35ae09d6a2eb3f --- /dev/null +++ b/briar-android/src/org/briarproject/android/fragment/DashboardFragment.java @@ -0,0 +1,66 @@ +package org.briarproject.android.fragment; + + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.GridView; + +import org.briarproject.R; +import org.briarproject.api.event.Event; +import org.briarproject.api.plugins.PluginManager; + +import java.util.logging.Logger; + +import javax.inject.Inject; + +import roboguice.inject.InjectView; + +public class DashboardFragment extends BaseEventFragment { + + public final static String TAG = "DashboardFragment"; + + private static final Logger LOG = + Logger.getLogger(DashboardFragment.class.getName()); + + @Inject + private PluginManager pluginManager; + + @InjectView(R.id.transportsView) + private GridView transportsView; + + public static DashboardFragment newInstance() { + + Bundle args = new Bundle(); + + DashboardFragment fragment = new DashboardFragment(); + fragment.setArguments(args); + return fragment; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View contentView = + inflater.inflate(R.layout.fragment_dashboard, container, false); + return contentView; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + } + + @Override + public String getUniqueTag() { + return TAG; + } + + @Override + public void eventOccurred(Event e) { + + } +} diff --git a/briar-android/src/org/briarproject/android/SettingsActivity.java b/briar-android/src/org/briarproject/android/fragment/SettingsFragment.java similarity index 80% rename from briar-android/src/org/briarproject/android/SettingsActivity.java rename to briar-android/src/org/briarproject/android/fragment/SettingsFragment.java index addccdc66bab944a325e198444a7cd07ebfb41bc..b3c5d946dad971797da44ad1c7dcbabc89a6369d 100644 --- a/briar-android/src/org/briarproject/android/SettingsActivity.java +++ b/briar-android/src/org/briarproject/android/fragment/SettingsFragment.java @@ -1,4 +1,4 @@ -package org.briarproject.android; +package org.briarproject.android.fragment; import android.bluetooth.BluetoothAdapter; import android.content.Intent; @@ -7,8 +7,10 @@ import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; +import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.ImageButton; import android.widget.LinearLayout; @@ -16,6 +18,7 @@ import android.widget.ScrollView; import android.widget.TextView; import org.briarproject.R; +import org.briarproject.android.TestingActivity; import org.briarproject.android.panic.PanicPreferencesActivity; import org.briarproject.android.util.AndroidUtils; import org.briarproject.android.util.FixedVerticalSpace; @@ -27,7 +30,6 @@ import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DbException; import org.briarproject.api.event.Event; import org.briarproject.api.event.EventBus; -import org.briarproject.api.event.EventListener; import org.briarproject.api.event.SettingsUpdatedEvent; import org.briarproject.api.settings.SettingsManager; import org.briarproject.util.StringUtils; @@ -53,16 +55,19 @@ import static android.widget.LinearLayout.VERTICAL; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static org.briarproject.android.TestingConstants.SHOW_TESTING_ACTIVITY; +import static android.app.Activity.RESULT_OK; import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP; import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP_1; -public class SettingsActivity extends BriarActivity implements EventListener, -OnClickListener { +public class SettingsFragment extends BaseFragment implements + View.OnClickListener { + + public final static String TAG = "SettingsFragment"; public static final int REQUEST_RINGTONE = 2; private static final Logger LOG = - Logger.getLogger(SettingsActivity.class.getName()); + Logger.getLogger(SettingsFragment.class.getName()); private ScrollView scroll = null; private TextView enableBluetooth = null, enableBluetoothHint = null; @@ -81,21 +86,36 @@ OnClickListener { private volatile Settings settings; private volatile boolean bluetoothSetting = true, torSetting = false; + public static SettingsFragment newInstance() { + + Bundle args = new Bundle(); + + SettingsFragment fragment = new SettingsFragment(); + fragment.setArguments(args); + return fragment; + } + @Override - public void onCreate(Bundle state) { - super.onCreate(state); + public String getUniqueTag() { + return TAG; + } - LinearLayout layout = new LinearLayout(this); + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + LinearLayout layout = new LinearLayout(getContext()); layout.setOrientation(VERTICAL); - scroll = new ScrollView(this); + scroll = new ScrollView(getContext()); - LinearLayout settings = new LinearLayout(this); + LinearLayout settings = new LinearLayout(getContext()); settings.setOrientation(VERTICAL); - int pad = LayoutUtils.getPadding(this); + int pad = LayoutUtils.getPadding(getContext()); settings.setPadding(pad, pad, pad, pad); - TextView bluetoothTitle = new TextView(this); + TextView bluetoothTitle = new TextView(getContext()); bluetoothTitle.setPadding(pad, 0, pad, 0); bluetoothTitle.setTypeface(DEFAULT_BOLD); Resources res = getResources(); @@ -104,143 +124,143 @@ OnClickListener { bluetoothTitle.setText(R.string.bluetooth_setting_title); settings.addView(bluetoothTitle); - HorizontalBorder underline = new HorizontalBorder(this); + HorizontalBorder underline = new HorizontalBorder(getContext()); int titleUnderline = res.getColor(R.color.settings_title_underline); underline.setBackgroundColor(titleUnderline); settings.addView(underline); - enableBluetooth = new TextView(this); + enableBluetooth = new TextView(getContext()); enableBluetooth.setPadding(pad, pad, pad, 0); enableBluetooth.setTextSize(18); enableBluetooth.setText(R.string.bluetooth_setting); enableBluetooth.setOnClickListener(this); settings.addView(enableBluetooth); - enableBluetoothHint = new TextView(this); + enableBluetoothHint = new TextView(getContext()); enableBluetoothHint.setPadding(pad, 0, pad, pad); enableBluetoothHint.setOnClickListener(this); settings.addView(enableBluetoothHint); - TextView torTitle = new TextView(this); + TextView torTitle = new TextView(getContext()); torTitle.setPadding(pad, 0, pad, 0); torTitle.setTypeface(DEFAULT_BOLD); torTitle.setTextColor(titleText); torTitle.setText(R.string.tor_wifi_setting_title); settings.addView(torTitle); - underline = new HorizontalBorder(this); + underline = new HorizontalBorder(getContext()); underline.setBackgroundColor(titleUnderline); settings.addView(underline); - torOverWifi = new TextView(this); + torOverWifi = new TextView(getContext()); torOverWifi.setPadding(pad, pad, pad, 0); torOverWifi.setTextSize(18); torOverWifi.setText(R.string.tor_wifi_setting); torOverWifi.setOnClickListener(this); settings.addView(torOverWifi); - torOverWifiHint = new TextView(this); + torOverWifiHint = new TextView(getContext()); torOverWifiHint.setPadding(pad, 0, pad, pad); torOverWifiHint.setOnClickListener(this); settings.addView(torOverWifiHint); - TextView panicTitle = new TextView(this); + TextView panicTitle = new TextView(getContext()); panicTitle.setPadding(pad, 0, pad, 0); panicTitle.setTypeface(DEFAULT_BOLD); panicTitle.setTextColor(titleText); panicTitle.setText(R.string.panic_setting_title); settings.addView(panicTitle); - underline = new HorizontalBorder(this); + underline = new HorizontalBorder(getContext()); underline.setBackgroundColor(titleUnderline); settings.addView(underline); - panicSettings = new TextView(this); + panicSettings = new TextView(getContext()); panicSettings.setPadding(pad, pad, pad, 0); panicSettings.setTextSize(18); panicSettings.setText(R.string.panic_setting); panicSettings.setOnClickListener(this); settings.addView(panicSettings); - panicSettingsHint = new TextView(this); + panicSettingsHint = new TextView(getContext()); panicSettingsHint.setText(R.string.panic_setting_hint); panicSettingsHint.setPadding(pad, 0, pad, pad); panicSettingsHint.setOnClickListener(this); settings.addView(panicSettingsHint); - TextView notificationsTitle = new TextView(this); + TextView notificationsTitle = new TextView(getContext()); notificationsTitle.setPadding(pad, 0, pad, 0); notificationsTitle.setTypeface(DEFAULT_BOLD); notificationsTitle.setTextColor(titleText); notificationsTitle.setText(R.string.notification_settings_title); settings.addView(notificationsTitle); - underline = new HorizontalBorder(this); + underline = new HorizontalBorder(getContext()); underline.setBackgroundColor(titleUnderline); settings.addView(underline); - settings.addView(new FixedVerticalSpace(this)); + settings.addView(new FixedVerticalSpace(getContext())); - notifyPrivateMessages = new CheckBox(this); + notifyPrivateMessages = new CheckBox(getContext()); notifyPrivateMessages.setTextSize(18); notifyPrivateMessages.setText(R.string.notify_private_messages_setting); notifyPrivateMessages.setOnClickListener(this); settings.addView(notifyPrivateMessages); - settings.addView(new FixedVerticalSpace(this)); - settings.addView(new HorizontalBorder(this)); - settings.addView(new FixedVerticalSpace(this)); + settings.addView(new FixedVerticalSpace(getContext())); + settings.addView(new HorizontalBorder(getContext())); + settings.addView(new FixedVerticalSpace(getContext())); - notifyForumPosts = new CheckBox(this); + notifyForumPosts = new CheckBox(getContext()); notifyForumPosts.setTextSize(18); notifyForumPosts.setText(R.string.notify_forum_posts_setting); notifyForumPosts.setOnClickListener(this); settings.addView(notifyForumPosts); - settings.addView(new FixedVerticalSpace(this)); - settings.addView(new HorizontalBorder(this)); - settings.addView(new FixedVerticalSpace(this)); + settings.addView(new FixedVerticalSpace(getContext())); + settings.addView(new HorizontalBorder(getContext())); + settings.addView(new FixedVerticalSpace(getContext())); - notifyVibration = new CheckBox(this); + notifyVibration = new CheckBox(getContext()); notifyVibration.setTextSize(18); notifyVibration.setText(R.string.notify_vibration_setting); notifyVibration.setOnClickListener(this); settings.addView(notifyVibration); - settings.addView(new FixedVerticalSpace(this)); - settings.addView(new HorizontalBorder(this)); + settings.addView(new FixedVerticalSpace(getContext())); + settings.addView(new HorizontalBorder(getContext())); - notifySound = new TextView(this); + notifySound = new TextView(getContext()); notifySound.setPadding(pad, pad, pad, 0); notifySound.setTextSize(18); notifySound.setText(R.string.notify_sound_setting); notifySound.setOnClickListener(this); settings.addView(notifySound); - notifySoundHint = new TextView(this); + notifySoundHint = new TextView(getContext()); notifySoundHint.setPadding(pad, 0, pad, pad); notifySoundHint.setOnClickListener(this); settings.addView(notifySoundHint); - settings.addView(new HorizontalBorder(this)); + settings.addView(new HorizontalBorder(getContext())); scroll.addView(settings); scroll.setLayoutParams(MATCH_WRAP_1); scroll.setVisibility(GONE); layout.addView(scroll); - progress = new ListLoadingProgressBar(this); + progress = new ListLoadingProgressBar(getContext()); layout.addView(progress); - layout.addView(new HorizontalBorder(this)); + layout.addView(new HorizontalBorder(getContext())); if (SHOW_TESTING_ACTIVITY) { - LinearLayout footer = new LinearLayout(this); + LinearLayout footer = new LinearLayout(getContext()); footer.setLayoutParams(MATCH_WRAP); footer.setGravity(CENTER); int background = res.getColor(R.color.button_bar_background); footer.setBackgroundColor(background); - testingButton = new ImageButton(this); + testingButton = new ImageButton(getContext()); testingButton.setBackgroundResource(0); testingButton.setImageResource(R.drawable.action_about); testingButton.setOnClickListener(this); @@ -248,18 +268,17 @@ OnClickListener { layout.addView(footer); } - setContentView(layout); + return layout; } @Override public void onResume() { super.onResume(); - eventBus.addListener(this); loadSettings(); } private void loadSettings() { - runOnDbThread(new Runnable() { + listener.runOnDbThread(new Runnable() { public void run() { try { long now = System.currentTimeMillis(); @@ -281,13 +300,14 @@ OnClickListener { } private void displaySettings() { - runOnUiThread(new Runnable() { + listener.runOnUiThread(new Runnable() { public void run() { scroll.setVisibility(VISIBLE); progress.setVisibility(GONE); int resId; - if (bluetoothSetting) resId = R.string.bluetooth_setting_enabled; + if (bluetoothSetting) + resId = R.string.bluetooth_setting_enabled; else resId = R.string.bluetooth_setting_disabled; enableBluetoothHint.setText(resId); @@ -318,16 +338,10 @@ OnClickListener { }); } - @Override - public void onPause() { - super.onPause(); - eventBus.removeListener(this); - } - public void onClick(View view) { if (progress == null) return; // Not created yet if (view == testingButton) { - startActivity(new Intent(this, TestingActivity.class)); + startActivity(new Intent(getActivity(), TestingActivity.class)); } else if (view == enableBluetooth || view == enableBluetoothHint) { bluetoothSetting = !bluetoothSetting; BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -346,7 +360,7 @@ OnClickListener { notifyPrivateMessages.isChecked()); storeSettings(s); } else if (view == panicSettings || view == panicSettingsHint) { - startActivity(new Intent(this, PanicPreferencesActivity.class)); + startActivity(new Intent(getActivity(), PanicPreferencesActivity.class)); } else if (view == notifyForumPosts) { Settings s = new Settings(); s.putBoolean("notifyForumPosts", notifyForumPosts.isChecked()); @@ -375,7 +389,7 @@ OnClickListener { } private void storeTorSettings() { - runOnDbThread(new Runnable() { + listener.runOnDbThread(new Runnable() { public void run() { try { Settings s = new Settings(); @@ -394,7 +408,7 @@ OnClickListener { } private void storeBluetoothSettings() { - runOnDbThread(new Runnable() { + listener.runOnDbThread(new Runnable() { public void run() { try { Settings s = new Settings(); @@ -413,11 +427,12 @@ OnClickListener { } private void storeSettings(final Settings settings) { - runOnDbThread(new Runnable() { + listener.runOnDbThread(new Runnable() { public void run() { try { long now = System.currentTimeMillis(); - settingsManager.mergeSettings(settings, "settings-activity"); + settingsManager + .mergeSettings(settings, "settings-activity"); long duration = System.currentTimeMillis() - now; if (LOG.isLoggable(INFO)) LOG.info("Merging settings took " + duration + " ms"); @@ -449,8 +464,8 @@ OnClickListener { s.put("notifyRingtoneUri", ""); } else { // The user chose a ringtone other than the default - Ringtone r = RingtoneManager.getRingtone(this, uri); - String name = r.getTitle(this); + Ringtone r = RingtoneManager.getRingtone(getContext(), uri); + String name = r.getTitle(getContext()); notifySoundHint.setText(name); s.putBoolean("notifySound", true); s.put("notifyRingtoneName", name); diff --git a/briar-android/src/org/briarproject/android/util/CustomAnimations.java b/briar-android/src/org/briarproject/android/util/CustomAnimations.java new file mode 100644 index 0000000000000000000000000000000000000000..78987efe6de3addfa83f4acfa10fc0bf5e93bb77 --- /dev/null +++ b/briar-android/src/org/briarproject/android/util/CustomAnimations.java @@ -0,0 +1,84 @@ +package org.briarproject.android.util; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.os.Build; +import android.view.View; +import android.view.ViewGroup; + +public class CustomAnimations { + + public static void animateHeight( + final ViewGroup viewGroup, final boolean isExtending, + int duration) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + animateHeightPostGingerbread(viewGroup, isExtending, duration); + } else { + animateHeightGingerbread(viewGroup, isExtending, duration); + } + } + + private static void animateHeightGingerbread(final ViewGroup viewGroup, + final boolean isExtending, int duration) { + // No animations for Gingerbread + if (isExtending) { + viewGroup.setVisibility(View.VISIBLE); + } else { + viewGroup.setVisibility(View.GONE); + } + } + + + @SuppressLint("NewApi") + private static void animateHeightPostGingerbread( + final ViewGroup viewGroup, + final boolean isExtending, + int duration) { + ValueAnimator anim; + if (isExtending) { + viewGroup.setVisibility(View.VISIBLE); + viewGroup.measure(View.MeasureSpec.UNSPECIFIED, + View.MeasureSpec.UNSPECIFIED); + anim = ValueAnimator.ofInt(0, viewGroup.getMeasuredHeight()); + } else { + anim = ValueAnimator.ofInt(viewGroup.getHeight(), 0); + } + anim.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!isExtending) { + viewGroup.setVisibility(View.GONE); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }); + anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + int val = (Integer) valueAnimator.getAnimatedValue(); + ViewGroup.LayoutParams layoutParams = + viewGroup.getLayoutParams(); + layoutParams.height = val; + viewGroup.setLayoutParams(layoutParams); + } + + }); + anim.setDuration(duration); + anim.start(); + } +}