diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 8fc720b8..6a7ba75d 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -24,10 +24,10 @@ I have: #### Expected result +**What is the expected output?** -What is the expected output? +**What do you see instead?** -What do you see instead? Upload screenshots via drag&drop if needed and apply resizing: `` diff --git a/.travis.yml b/.travis.yml index 74b79b85..41f1aaaa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ android: - tools - tools # TODO https://github.com/travis-ci/travis-ci/issues/6193 - platform-tools - - build-tools-24.0.1 + - build-tools-24.0.2 - android-24 - extra-android-m2repository before_cache: diff --git a/README.md b/README.md index a2696020..cc8704d6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ [![F-Droid](https://f-droid.org/wiki/images/0/06/F-Droid-button_get-it-on.png)](https://f-droid.org/repository/browse/?fdid=com.github.dfa.diaspora_android) -[![Build Status](https://travis-ci.org/Diaspora-for-Android/diaspora-android.svg?branch=master)](https://travis-ci.org/Diaspora-for-Android/diaspora-android) +[![Build Status](https://travis-ci.org/Diaspora-for-Android/diaspora-android.svg?branch=master)](https://travis-ci.org/Diaspora-for-Android/diaspora-android) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/diaspora-for-android/localized.svg)](https://crowdin.com/project/diaspora-for-android) -[![Join the chat at https://gitter.im/Diaspora-for-Android/diaspora-android](https://badges.gitter.im/Diaspora-for-Android/diaspora-android.svg)](https://gitter.im/Diaspora-for-Android/diaspora-android?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Chat - FreeNode IRC](https://img.shields.io/badge/chat-on%20freenode-blue.svg)](https://kiwiirc.com/client/irc.freenode.net/?nick=user-dfa|?#diaspora-for-android) +[![Chat - Gitter](https://img.shields.io/badge/chat-on%20gitter-blue.svg)](https://gitter.im/Diaspora-for-Android/diaspora-android) # Diaspora for Android @@ -15,28 +16,28 @@ This is an unofficial webview based client for the community-run, distributed so ## Contributions We are always open for any kind of contribution. (PR's, bug reports, feature requests, translations, ..) -If you got any questions feel free to join our XMPP/Jabber conference at `diaspora-android@conference.jabberhead.tk` or [Gitter](https://gitter.im/Diaspora-for-Android/diaspora-android). -Note that the main project members are mostly busy with their job/university/school and may not react or start coding immediately. +If you got any questions feel free to contact us on IRC, XMPP or Gitter. You can start chatting by clicking on the [blue chat badges](#badgers) listed on top. -We use Crowdin to translate Diaspora for Android. Join our project here: -If your desired language is not listed please contact the maintainers/owner. +We use Crowdin to translate Diaspora for Android. Join our project here: . If your desired language is not listed please contact the maintainers/owner. + +Note that the main project members are working on this project for free during leisure time, are mostly busy with their job/university/school, and may not react or start coding immediately. ### License -It's released under GNU GENERAL PUBLIC LICENSE (see [LICENCE](https://github.com/Diaspora-for-Android/diaspora-android/blob/master/LICENSE.md)). +Diaspora for Android is released under GNU GENERAL PUBLIC LICENSE (see [LICENCE](https://github.com/Diaspora-for-Android/diaspora-android/blob/master/LICENSE.md)). ### WebApp -The app is developed as an WebApp because currently Diaspora doesn't have an API that can be used to create a native interface to retrieve the user's data, publications, direct messages and so on, that's why there are only WebApps for Diaspora out there. +The app is developed as a WebApp because currently Diaspora doesn't have an API that can be used to create a native interface to retrieve the user's data, publications, direct messages and so on. That's why there are only WebApps for Diaspora out there. [Stay tuned on Diaspora* Issues](https://github.com/diaspora/diaspora/labels/api) about API. -Why a WebApp is better than using the mobile site on a browser? +Why is a WebApp better than using the mobile site on a browser? Basically it provides better integration with the system (events coming into and going out of the app), notifications, customized interface and functions and a nice little icon that takes you directly to your favorite social network :) ### Device Requirements -The minimum version supported is Jelly Bean, Android v4.2.0 / API 17 +The minimum Android version supported is Jelly Bean, Android v4.2.0 / API 17 ### App Permissions -It requires access to the Internet and to external storage to be able to upload photos when creating a new post and for taking screenshots. +Diaspora for Android requires access to the Internet and to external storage to be able to upload photos when creating a new post and for taking screenshots. ## Maintainers -- gsantner ([GitHub](https://github.com/gsantner), [Web](https://gsantner.github.io)) -- vanitasvitae ([GitHub](https://github.com/vanitasvitae)) +- gsantner ([GitHub](https://github.com/gsantner), [Web](https://gsantner.github.io), [diaspora*](https://pod.geraspora.de/people/d1cbdd70095301341e834860008dbc6c)) +- vanitasvitae ([GitHub](https://github.com/vanitasvitae), [Diaspora](https://pod.geraspora.de/people/bbd7af90fbec013213e34860008dbc6c)) diff --git a/SCREENSHOTS.md b/SCREENSHOTS.md index 368da74a..8f443531 100644 --- a/SCREENSHOTS.md +++ b/SCREENSHOTS.md @@ -1,14 +1,24 @@ - - - - + + + + - - - - + + + + + + + + + + + + + +
diff --git a/app/build.gradle b/app/build.gradle index 4cb94656..0e7cceec 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,14 +3,14 @@ apply plugin: 'android-apt' android { compileSdkVersion 24 - buildToolsVersion "24.0.1" + buildToolsVersion "24.0.2" defaultConfig { applicationId "com.github.dfa.diaspora_android" minSdkVersion 17 targetSdkVersion 24 versionCode 8 - versionName "0.1.6" + versionName "0.1.6-next" } buildTypes { release { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2ef6cc59..3ee52fa8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,52 +12,40 @@ android:name="com.github.dfa.diaspora_android.App" android:label="@string/app_name" android:theme="@style/AppTheme" > + - - - - - - - - + + - + android:label="@string/settings" /> - - + + + + + + @@ -255,9 +243,7 @@ - + + \ No newline at end of file diff --git a/app/src/main/java/com/github/dfa/diaspora_android/activity/AboutActivity.java b/app/src/main/java/com/github/dfa/diaspora_android/activity/AboutActivity.java index f2c46bdd..cc9cf36d 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/activity/AboutActivity.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/activity/AboutActivity.java @@ -62,10 +62,10 @@ public class AboutActivity extends AppCompatActivity { private SectionsPagerAdapter mSectionsPagerAdapter; private ViewPager mViewPager; - @BindView(R.id.toolbar) + @BindView(R.id.main__topbar) protected Toolbar toolbar; - @BindView(R.id.linearlayout) + @BindView(R.id.appbar_linear_layout) protected LinearLayout linearLayout; @Override diff --git a/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java b/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java index c736489e..ca01035f 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java @@ -18,33 +18,24 @@ */ package com.github.dfa.diaspora_android.activity; -import android.Manifest; -import android.animation.ObjectAnimator; import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.AlarmManager; -import android.app.AlertDialog; -import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.graphics.Bitmap; import android.graphics.drawable.LayerDrawable; import android.net.Uri; -import android.os.Build; import android.os.Bundle; -import android.os.Environment; import android.os.Handler; -import android.os.StrictMode; -import android.provider.MediaStore; import android.support.annotation.NonNull; +import android.support.customtabs.CustomTabsSession; import android.support.design.widget.AppBarLayout; import android.support.design.widget.NavigationView; import android.support.design.widget.Snackbar; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; @@ -59,17 +50,10 @@ import android.view.View; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; -import android.webkit.JavascriptInterface; -import android.webkit.ValueCallback; -import android.webkit.WebChromeClient; -import android.webkit.WebSettings; -import android.webkit.WebView; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; @@ -77,43 +61,32 @@ import com.github.dfa.diaspora_android.App; import com.github.dfa.diaspora_android.R; import com.github.dfa.diaspora_android.data.AppSettings; import com.github.dfa.diaspora_android.data.PodUserProfile; +import com.github.dfa.diaspora_android.fragment.BrowserFragment; +import com.github.dfa.diaspora_android.fragment.CustomFragment; +import com.github.dfa.diaspora_android.fragment.DiasporaStreamFragment; +import com.github.dfa.diaspora_android.fragment.HashtagListFragment; +import com.github.dfa.diaspora_android.fragment.PodSelectionFragment; import com.github.dfa.diaspora_android.listener.WebUserProfileChangedListener; -import com.github.dfa.diaspora_android.receivers.OpenExternalLinkReceiver; -import com.github.dfa.diaspora_android.receivers.UpdateTitleReceiver; +import com.github.dfa.diaspora_android.receiver.OpenExternalLinkReceiver; +import com.github.dfa.diaspora_android.receiver.UpdateTitleReceiver; import com.github.dfa.diaspora_android.ui.BadgeDrawable; -import com.github.dfa.diaspora_android.ui.ContextMenuWebView; -import com.github.dfa.diaspora_android.ui.CustomWebViewClient; import com.github.dfa.diaspora_android.util.AppLog; import com.github.dfa.diaspora_android.util.CustomTabHelpers.CustomTabActivityHelper; import com.github.dfa.diaspora_android.util.DiasporaUrlHelper; -import com.github.dfa.diaspora_android.util.Helpers; import com.github.dfa.diaspora_android.util.WebHelper; -import org.json.JSONException; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; -import info.guardianproject.netcipher.NetCipher; -import info.guardianproject.netcipher.webkit.WebkitProxy; public class MainActivity extends AppCompatActivity - implements NavigationView.OnNavigationItemSelectedListener, WebUserProfileChangedListener { + implements NavigationView.OnNavigationItemSelectedListener, WebUserProfileChangedListener, CustomTabActivityHelper.ConnectionCallback { - private static final int INPUT_FILE_REQUEST_CODE_NEW = 1; - private static final int INPUT_FILE_REQUEST_CODE_OLD = 2; - private static final int REQUEST_CODE_ASK_PERMISSIONS = 123; + public static final int REQUEST_CODE_ASK_PERMISSIONS = 123; public static final int REQUEST_CODE__ACCESS_EXTERNAL_STORAGE = 124; + public static final int INPUT_FILE_REQUEST_CODE_NEW = 1; + public static final int INPUT_FILE_REQUEST_CODE_OLD = 2; public static final String ACTION_OPEN_URL = "com.github.dfa.diaspora_android.MainActivity.open_url"; public static final String ACTION_OPEN_EXTERNAL_URL = "com.github.dfa.diaspora_android.MainActivity.open_external_url"; @@ -126,46 +99,34 @@ public class MainActivity extends AppCompatActivity public static final String CONTENT_HASHTAG = "content://com.github.dfa.diaspora_android.mainactivity/"; private App app; - private ValueCallback imageUploadFilePathCallbackNew; - private ValueCallback imageUploadFilePathCallbackOld; - private String mCameraPhotoPath; private CustomTabActivityHelper customTabActivityHelper; - private WebSettings webSettings; private AppSettings appSettings; private DiasporaUrlHelper urls; private PodUserProfile podUserProfile; private final Handler uiHandler = new Handler(); - private CustomWebViewClient webViewClient; private OpenExternalLinkReceiver brOpenExternalLink; private BroadcastReceiver brSetTitle; private Snackbar snackbarExitApp; private Snackbar snackbarNoInternet; - private String textToBeShared = null; + private FragmentManager fm; + private CustomTabsSession customTabsSession; /** * UI Bindings */ - @BindView(R.id.content_layout) - RelativeLayout contentLayout; - - @BindView(R.id.progressBar) - ProgressBar progressBar; - - @BindView(R.id.toolbar) + @BindView(R.id.main__topbar) Toolbar toolbarTop; - @BindView(R.id.toolbar2) + @BindView(R.id.main__bottombar) ActionMenuView toolbarBottom; - @BindView(R.id.placeholder_webview) - FrameLayout webviewPlaceholder; - - ContextMenuWebView webView; + @BindView(R.id.fragment_container) + FrameLayout fragmentContainer; @BindView(R.id.main__navigaion_view) NavigationView navView; - @BindView(R.id.main__layout) + @BindView(R.id.main__navdrawer) DrawerLayout navDrawer; @@ -174,6 +135,8 @@ public class MainActivity extends AppCompatActivity private TextView navheaderDescription; private ImageView navheaderImage; + private String textToBeShared; + /** * END UI Bindings @@ -187,6 +150,7 @@ public class MainActivity extends AppCompatActivity // Bind UI setContentView(R.layout.main__activity); + ButterKnife.bind(this); if ((app = (App) getApplication()) == null) AppLog.e(this, "App is null!"); if ((appSettings = app.getSettings()) == null) AppLog.e(this, "AppSettings is null!"); @@ -196,68 +160,65 @@ public class MainActivity extends AppCompatActivity podUserProfile.setListener(this); urls = new DiasporaUrlHelper(appSettings); customTabActivityHelper = new CustomTabActivityHelper(); + customTabActivityHelper.setConnectionCallback(this); - setupUI(savedInstanceState); - - if (appSettings.isProxyEnabled()) { - if (!setProxy(appSettings.getProxyHost(), appSettings.getProxyPort())) { - AppLog.e(this, "Could not enable Proxy"); - Toast.makeText(MainActivity.this, R.string.toast_set_proxy_failed, Toast.LENGTH_SHORT).show(); - } - } else if (appSettings.wasProxyEnabled()) { - resetProxy(); - } + fm = getSupportFragmentManager(); + setupUI(); brOpenExternalLink = new OpenExternalLinkReceiver(this); brSetTitle = new UpdateTitleReceiver(app, urls, new UpdateTitleReceiver.TitleCallback() { @Override public void setTitle(int rId) { - MainActivity.this.setTitle(rId); + CustomFragment top = getTopFragment(); + if(top != null && top.getFragmentTag().equals(DiasporaStreamFragment.TAG)) { + MainActivity.this.setTitle(rId); + } } @Override public void setTitle(String title) { - MainActivity.this.setTitle(title); + CustomFragment top = getTopFragment(); + if(top != null && top.getFragmentTag().equals(DiasporaStreamFragment.TAG)) { + MainActivity.this.setTitle(title); + } } }); + + if(!appSettings.hasPodDomain()) { + AppLog.d(this, "We have no pod. Show PodSelectionFragment"); + showFragment(getFragment(PodSelectionFragment.TAG)); + } else { + AppLog.d(this, "Pod found. Handle intents."); + //Handle intent + Intent intent = getIntent(); + if (intent != null && intent.getAction() != null) { + handleIntent(intent); + } else { + openDiasporaUrl(urls.getStreamUrl()); + } + } } - private void setupUI(Bundle savedInstanceState) { + /** + * Setup the user interface. Set up both toolbars and initialize the snackbars. + * Initialize the navigation drawer and apply intellihide settings. + */ + private void setupUI() { AppLog.i(this, "setupUI()"); - ButterKnife.bind(this); - if (webviewPlaceholder.getChildCount() != 0) { - AppLog.v(this, "remove child views from webViewPlaceholder"); - webviewPlaceholder.removeAllViews(); - } else { - AppLog.v(this, "webViewPlaceholder had no child views"); - } - boolean newWebView = (webView == null); - if (newWebView) { - AppLog.v(this, "WebView was null. Create new one."); - View webviewHolder = getLayoutInflater().inflate(R.layout.ui__webview, this.contentLayout, false); - this.webView = (ContextMenuWebView) webviewHolder.findViewById(R.id.webView); - ((LinearLayout) webView.getParent()).removeView(webView); - setupWebView(savedInstanceState); - } else { - AppLog.v(this, "Reuse old WebView to avoid reloading page"); - } - - AppLog.v(this, "Add WebView to placeholder"); - webviewPlaceholder.addView(webView); // Setup toolbar setSupportActionBar(toolbarTop); - getMenuInflater().inflate(R.menu.main__menu_bottom, toolbarBottom.getMenu()); toolbarBottom.setOnMenuItemClickListener(new ActionMenuView.OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { - return MainActivity.this.onOptionsItemSelected(item); + CustomFragment top = getTopFragment(); + return MainActivity.this.onOptionsItemSelected(item) || (top != null && top.onOptionsItemSelected(item)); } }); setTitle(R.string.app_name); //Setup snackbar snackbarExitApp = Snackbar - .make(contentLayout, R.string.confirm_exit, Snackbar.LENGTH_LONG) + .make(fragmentContainer, R.string.confirm_exit, Snackbar.LENGTH_LONG) .setAction(android.R.string.yes, new View.OnClickListener() { @Override public void onClick(View view) { @@ -265,24 +226,11 @@ public class MainActivity extends AppCompatActivity moveTaskToBack(true); } }); - snackbarNoInternet = Snackbar.make(contentLayout, R.string.no_internet, Snackbar.LENGTH_LONG); + snackbarNoInternet = Snackbar.make(fragmentContainer, R.string.no_internet, Snackbar.LENGTH_LONG); // Load app settings setupNavigationSlider(); - progressBar = (ProgressBar) findViewById(R.id.progressBar); - - String url = urls.getPodUrl(); - if (newWebView) { - if (WebHelper.isOnline(MainActivity.this)) { - AppLog.v(this, "setupUI: reload url"); - webView.loadData("", "text/html", null); - webView.loadUrlNew(url); - } else { - snackbarNoInternet.show(); - } - } - if (!appSettings.isIntellihideToolbars()) { AppLog.v(this, "Disable intelligent hiding of toolbars"); AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbarTop.getLayoutParams(); @@ -290,183 +238,75 @@ public class MainActivity extends AppCompatActivity } AppLog.v(this, "UI successfully set up"); - handleIntent(getIntent()); } - @Override - public void onConfigurationChanged(Configuration newConfig) { - AppLog.i(this, "onConfigurationChanged()"); - if (webView != null) { - // Remove the WebView from the old placeholder - AppLog.v(this, "removeView from placeholder in order to prevent recreation"); - webviewPlaceholder.removeView(webView); - } - - super.onConfigurationChanged(newConfig); - - // Load the layout resource for the new configuration - setContentView(R.layout.main__activity); - - // Reinitialize the UI - AppLog.v(this, "Rebuild the UI"); - setupUI(null); + /** + * Show DiasporaStreamFragment if necessary and load URL url + * @param url URL to load in the DiasporaStreamFragment + */ + public void openDiasporaUrl(String url) { + AppLog.v(this, "openDiasporaUrl()"); + DiasporaStreamFragment streamFragment = (DiasporaStreamFragment) getFragment(DiasporaStreamFragment.TAG); + showFragment(streamFragment); + streamFragment.loadUrl(url); } - private void setupWebView(Bundle savedInstanceState) { - - webSettings = webView.getSettings(); - webSettings.setJavaScriptEnabled(true); - webSettings.setAllowFileAccess(false); - webSettings.setUseWideViewPort(true); - webSettings.setLoadWithOverviewMode(true); - webSettings.setDomStorageEnabled(true); - webSettings.setMinimumFontSize(appSettings.getMinimumFontSize()); - webSettings.setLoadsImagesAutomatically(appSettings.isLoadImages()); - webSettings.setAppCacheEnabled(true); - - if (savedInstanceState != null) { - AppLog.v(this, "restore WebView state"); - webView.restoreState(savedInstanceState); - } - - if (android.os.Build.VERSION.SDK_INT >= 21) { - WebView.enableSlowWholeDocumentDraw(); - webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); - } - - this.registerForContextMenu(webView); - webView.setParentActivity(this); - webView.setOverScrollMode(WebView.OVER_SCROLL_ALWAYS); - - // Setup WebView - webView.addJavascriptInterface(new JavaScriptInterface(), "AndroidBridge"); - - //Set proxy - if (appSettings.isProxyEnabled()) { - if (!setProxy()) - Toast.makeText(this, R.string.toast_set_proxy_failed, Toast.LENGTH_LONG).show(); - } - - /* - * WebViewClient - */ - webViewClient = new CustomWebViewClient(app, webView); - webView.setWebViewClient(webViewClient); - - /* - * WebChromeClient - */ - webView.setWebChromeClient(new WebChromeClient() { - - public void onProgressChanged(WebView wv, int progress) { - progressBar.setProgress(progress); - - if (progress > 0 && progress <= 60) { - WebHelper.getUserProfile(wv); - WebHelper.optimizeMobileSiteLayout(wv); - } - - if (progress > 60) { - WebHelper.optimizeMobileSiteLayout(wv); - - if (textToBeShared != null) { - WebHelper.shareTextIntoWebView(wv, textToBeShared); - } - } - - progressBar.setVisibility(progress == 100 ? View.GONE : View.VISIBLE); + /** + * Get an instance of the CustomFragment with the tag fragmentTag. + * If there was no instance so far, create a new one and add it to the FragmentManagers pool. + * If there is no Fragment with the corresponding Tag, return the top fragment. + * @param fragmentTag tag + * @return corresponding Fragment + */ + protected CustomFragment getFragment(String fragmentTag) { + CustomFragment fragment = (CustomFragment) fm.findFragmentByTag(fragmentTag); + if(fragment != null) { + return fragment; + } else { + switch (fragmentTag) { + case DiasporaStreamFragment.TAG: + DiasporaStreamFragment dsf = new DiasporaStreamFragment(); + fm.beginTransaction().add(dsf, fragmentTag).commit(); + return dsf; + case BrowserFragment.TAG: + BrowserFragment bf = new BrowserFragment(); + fm.beginTransaction().add(bf, fragmentTag).commit(); + return bf; + case HashtagListFragment.TAG: + HashtagListFragment hlf = new HashtagListFragment(); + fm.beginTransaction().add(hlf, fragmentTag).commit(); + return hlf; + case PodSelectionFragment.TAG: + PodSelectionFragment psf = new PodSelectionFragment(); + fm.beginTransaction().add(psf, fragmentTag).commit(); + return psf; + default: + AppLog.e(this,"Invalid Fragment Tag: "+fragmentTag + +"\nAdd Fragments Tag to getFragment()'s switch case."); + return getTopFragment(); } - - //For Android 4.1/4.2 only. DO NOT REMOVE! - @SuppressWarnings("unused") - protected void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) { - AppLog.v(this, "openFileChooser(ValCallback, String, String"); - imageUploadFilePathCallbackOld = uploadMsg; - Intent intent = new Intent(); - intent.setType("image/*"); - intent.setAction(Intent.ACTION_GET_CONTENT); - intent.putExtra("return-data", true); - intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - AppLog.v(this, "startActivityForResult"); - startActivityForResult(Intent.createChooser(intent, "Select Picture"), INPUT_FILE_REQUEST_CODE_OLD); - } - - @Override - public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { - if (Build.VERSION.SDK_INT >= 23) { - int hasWRITE_EXTERNAL_STORAGE = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE); - if (hasWRITE_EXTERNAL_STORAGE != PackageManager.PERMISSION_GRANTED) { - if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - new AlertDialog.Builder(MainActivity.this) - .setMessage(R.string.permissions_image) - .setNegativeButton(android.R.string.no, null) - .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (android.os.Build.VERSION.SDK_INT >= 23) - requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, - REQUEST_CODE_ASK_PERMISSIONS); - } - }) - .show(); - return false; - } - requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, - REQUEST_CODE_ASK_PERMISSIONS); - return false; - } - } - - AppLog.v(MainActivity.this, "onOpenFileChooser"); - imageUploadFilePathCallbackNew = filePathCallback; - - Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - if (takePictureIntent.resolveActivity(getPackageManager()) != null) { - // Create the File where the photo should go - File photoFile; - try { - photoFile = Helpers.createImageFile(); - takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath); - } catch (IOException ex) { - AppLog.e(this, "ERROR creating temp file: " + ex.toString()); - // Error occurred while creating the File - Snackbar.make(contentLayout, R.string.unable_to_load_image, Snackbar.LENGTH_LONG).show(); - return false; - } - - // Continue only if the File was successfully created - if (photoFile != null) { - mCameraPhotoPath = "file:" + photoFile.getAbsolutePath(); - takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, - Uri.fromFile(photoFile)); - } else { - takePictureIntent = null; - } - } - - Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT); - contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE); - contentSelectionIntent.setType("image/*"); - - Intent[] intentArray; - if (takePictureIntent != null) { - intentArray = new Intent[]{takePictureIntent}; - } else { - intentArray = new Intent[0]; - } - - Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER); - chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent); - chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser"); - chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); - - AppLog.v(this, "startActivityForResult"); - startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE_NEW); - return true; - } - }); + } } + /** + * Show the Fragment fragment in R.id.fragment_container. If the fragment was already visible, do nothing. + * @param fragment Fragment to show + */ + protected void showFragment(CustomFragment fragment) { + AppLog.v(this, "showFragment()"); + CustomFragment currentTop = (CustomFragment) fm.findFragmentById(R.id.fragment_container); + if(currentTop == null || !currentTop.getFragmentTag().equals(fragment.getFragmentTag())) { + AppLog.v(this, "Fragment was not visible. Replace it."); + fm.beginTransaction().addToBackStack(null).replace(R.id.fragment_container, fragment, fragment.getFragmentTag()).commit(); + invalidateOptionsMenu(); + } else { + AppLog.v(this, "Fragment was already visible. Do nothing."); + } + } + + /** + * Initialize the navigation slider + */ private void setupNavigationSlider() { ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, navDrawer, toolbarTop, R.string.navigation_drawer_open, R.string.navigation_drawer_close); @@ -478,12 +318,13 @@ public class MainActivity extends AppCompatActivity View navHeader = navView.getHeaderView(0); LinearLayout navheaderProfileSection = ButterKnife.findById(navHeader, R.id.nav_profile_picture); + //Handle clicks on profile picture navheaderProfileSection.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { navDrawer.closeDrawer(GravityCompat.START); if (WebHelper.isOnline(MainActivity.this)) { - webView.loadUrlNew(urls.getProfileUrl()); + openDiasporaUrl(urls.getProfileUrl()); } else { snackbarNoInternet.show(); } @@ -523,26 +364,27 @@ public class MainActivity extends AppCompatActivity navMenu.findItem(R.id.nav_aspects).setVisible(appSettings.isVisibleInNavAspects()); navMenu.findItem(R.id.nav_commented).setVisible(appSettings.isVisibleInNavCommented()); navMenu.findItem(R.id.nav_followed_tags).setVisible(appSettings.isVisibleInNavFollowed_tags()); - navMenu.findItem(R.id.nav_help_license).setVisible(appSettings.isVisibleInNavHelp_license()); + navMenu.findItem(R.id.nav_about).setVisible(appSettings.isVisibleInNavHelp_license()); navMenu.findItem(R.id.nav_liked).setVisible(appSettings.isVisibleInNavLiked()); navMenu.findItem(R.id.nav_mentions).setVisible(appSettings.isVisibleInNavMentions()); navMenu.findItem(R.id.nav_profile).setVisible(appSettings.isVisibleInNavProfile()); navMenu.findItem(R.id.nav_public).setVisible(appSettings.isVisibleInNavPublic_activities()); } - - @OnClick(R.id.toolbar) - public void onToolBarClicked(View view) { - AppLog.i(this, "onToolBarClicked()"); - onNavigationItemSelected(navView.getMenu().findItem(R.id.nav_stream)); - } - + + /** + * Forward incoming intents to handleIntent() + * @param intent incoming + */ @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - handleIntent(intent); } + /** + * Handle intents and execute intent specific actions + * @param intent intent to get handled + */ private void handleIntent(Intent intent) { AppLog.i(this, "handleIntent()"); if (intent == null) { @@ -554,7 +396,9 @@ public class MainActivity extends AppCompatActivity String type = intent.getType(); String loadUrl = null; AppLog.v(this, "Action: " + action + " Type: " + type); - if (ACTION_OPEN_URL.equals(action)) { + if (Intent.ACTION_MAIN.equals(action)) { + loadUrl = urls.getStreamUrl(); + } else if (ACTION_OPEN_URL.equals(action)) { loadUrl = intent.getStringExtra(URL_MESSAGE); } else if (Intent.ACTION_VIEW.equals(action) && intent.getDataString() != null) { Uri data = intent.getData(); @@ -563,14 +407,15 @@ public class MainActivity extends AppCompatActivity return; } else { loadUrl = intent.getDataString(); + AppLog.v(this, "Intent has a delicious URL for us: "+loadUrl); } } else if (ACTION_CHANGE_ACCOUNT.equals(action)) { - AppLog.v(this, "Reset pod data and animate to PodSelectionActivity"); - app.resetPodData(webView); - Helpers.animateToActivity(MainActivity.this, PodSelectionActivity.class, true); + AppLog.v(this, "Reset pod data and show PodSelectionFragment"); + app.resetPodData(((DiasporaStreamFragment) getFragment(DiasporaStreamFragment.TAG)).getWebView()); + showFragment(getFragment(PodSelectionFragment.TAG)); } else if (ACTION_CLEAR_CACHE.equals(action)) { AppLog.v(this, "Clear WebView cache"); - webView.clearCache(true); + ((DiasporaStreamFragment) getFragment(DiasporaStreamFragment.TAG)).getWebView().clearCache(true); } else if (ACTION_RELOAD_ACTIVITY.equals(action)) { AppLog.v(this, "Recreate activity"); recreate(); @@ -589,101 +434,43 @@ public class MainActivity extends AppCompatActivity break; } } else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) { - //TODO: Implement and add filter to manifest + /* TODO: Implement and add filter to manifest */ + return; } if (loadUrl != null) { - webView.stopLoading(); navDrawer.closeDrawers(); - webView.loadUrlNew(loadUrl); + openDiasporaUrl(loadUrl); } } + /** + * Handle activity results + * @param requestCode reqCode + * @param resultCode resCode + * @param data data + */ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { - AppLog.v(this, "onActivityResult()"); - switch (requestCode) { - case INPUT_FILE_REQUEST_CODE_NEW: { - AppLog.v(this, "Upload image using recent method (Lollipop+)"); - if (imageUploadFilePathCallbackNew == null || resultCode != Activity.RESULT_OK) { - AppLog.e(this, "Callback is null: " + (imageUploadFilePathCallbackNew == null) - + " resultCode: " + resultCode); - if(imageUploadFilePathCallbackNew != null) - imageUploadFilePathCallbackNew.onReceiveValue(new Uri[]{}); - return; - } - Uri[] results = null; - if (data == null) { - if (mCameraPhotoPath != null) { - AppLog.v(this, "Intent data is null. Try to parse cameraPhotoPath"); - results = new Uri[]{Uri.parse(mCameraPhotoPath)}; - } else { - AppLog.w(this, "Intent data is null and cameraPhotoPath is null"); - } - } else { - String dataString = data.getDataString(); - if (dataString != null) { - AppLog.v(this, "Intent has data. Try to parse dataString"); - results = new Uri[]{Uri.parse(dataString)}; - } - AppLog.w(this, "dataString is null"); - } - AppLog.v(this, "handle received result over to callback"); - imageUploadFilePathCallbackNew.onReceiveValue(results); - imageUploadFilePathCallbackNew = null; - return; - } - case INPUT_FILE_REQUEST_CODE_OLD: { - AppLog.v(this, "Upload image using legacy method (Jelly Bean, Kitkat)"); - if (imageUploadFilePathCallbackOld == null || resultCode != Activity.RESULT_OK) { - AppLog.e(this, "Callback is null: " + (imageUploadFilePathCallbackOld == null) - + " resultCode: " + resultCode); - if(imageUploadFilePathCallbackOld != null) - imageUploadFilePathCallbackOld.onReceiveValue(null); - return; - } - Uri results = null; - if (data == null) { - if (mCameraPhotoPath != null) { - AppLog.v(this, "Intent has no data. Try to parse cameraPhotoPath"); - results = Uri.parse(mCameraPhotoPath); - } else { - AppLog.w(this, "Intent has no data and cameraPhotoPath is null"); - } - } else { - String dataString = data.getDataString(); - if (dataString != null) { - AppLog.v(this, "Intent has data. Try to parse dataString"); - results = Uri.parse(dataString); - } else { - AppLog.w(this, "dataString is null"); - } - } - AppLog.v(this, "handle received result over to callback"); - imageUploadFilePathCallbackOld.onReceiveValue(results); - imageUploadFilePathCallbackOld = null; - return; - } - } + AppLog.v(this, "onActivityResult(): "+requestCode); super.onActivityResult(requestCode, resultCode, data); } - @Override - protected void onSaveInstanceState(Bundle outState) { - AppLog.v(this, "onSaveInstanceState()"); - super.onSaveInstanceState(outState); - AppLog.v(this, "Save WebView state"); - webView.saveState(outState); - } - - @Override - protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { - AppLog.v(this, "onRestoreInstanceState()"); - super.onRestoreInstanceState(savedInstanceState); - AppLog.v(this, "Restore state of WebView"); - webView.restoreState(savedInstanceState); + /** + * Return the fragment which is currently displayed in R.id.fragment_container + * @return top fragment or null if there is none displayed + */ + private CustomFragment getTopFragment() { + Fragment top = fm.findFragmentById(R.id.fragment_container); + if(top != null) { + return (CustomFragment) top; + } + return null; } + /** + * Handle presses on the back button + */ @Override public void onBackPressed() { AppLog.v(this, "onBackPressed()"); @@ -691,10 +478,22 @@ public class MainActivity extends AppCompatActivity navDrawer.closeDrawer(navView); return; } - - if (webView.canGoBack()) { - webView.goBack(); - return; + CustomFragment top = getTopFragment(); + if(top != null) { + AppLog.v(this, "Top Fragment is not null"); + if(!top.onBackPressed()) { + AppLog.v(this, "Top Fragment.onBackPressed was false"); + AppLog.v(this, "BackStackEntryCount: "+fm.getBackStackEntryCount()); + if(fm.getBackStackEntryCount()>0) { + fm.popBackStack(); + } else { + snackbarExitApp.show(); + } + return; + } else { + AppLog.v(this, "Top Fragment.onBackPressed was true"); + return; + } } if (!snackbarExitApp.isShown()) { @@ -732,13 +531,42 @@ public class MainActivity extends AppCompatActivity LocalBroadcastManager.getInstance(this).registerReceiver(brOpenExternalLink, new IntentFilter(ACTION_OPEN_EXTERNAL_URL)); } + /** + * Clear and repopulate top and bottom toolbar. + * Also add menu items of the displayed fragment + * @param menu top toolbar + * @return boolean + */ @Override public boolean onCreateOptionsMenu(Menu menu) { AppLog.v(this, "onCreateOptionsMenu()"); - getMenuInflater().inflate(R.menu.main__menu_top, menu); + //Clear the menus + menu.clear(); + toolbarBottom.getMenu().clear(); + toolbarBottom.setVisibility(View.VISIBLE); + + CustomFragment top = getTopFragment(); + if(top != null) { + //Are we displaying a Fragment other than PodSelectionFragment? + if(!top.getFragmentTag().equals(PodSelectionFragment.TAG)) { + getMenuInflater().inflate(R.menu.main__menu_top, menu); + getMenuInflater().inflate(R.menu.main__menu_bottom, toolbarBottom.getMenu()); + top.onCreateBottomOptionsMenu(toolbarBottom.getMenu(), getMenuInflater()); + } + //PodSelectionFragment + else { + //Hide bottom toolbar + toolbarBottom.setVisibility(View.GONE); + } + } return true; } + /** + * Set the notification and messages counter in the top toolbar + * @param menu menu + * @return boolean + */ @Override public boolean onPrepareOptionsMenu(Menu menu) { MenuItem item; @@ -755,13 +583,18 @@ public class MainActivity extends AppCompatActivity return super.onPrepareOptionsMenu(menu); } + /** + * Handle clicks on the optionsmenu + * @param item item + * @return boolean + */ @Override public boolean onOptionsItemSelected(MenuItem item) { AppLog.i(this, "onOptionsItemSelected()"); switch (item.getItemId()) { case R.id.action_notifications: { if (WebHelper.isOnline(MainActivity.this)) { - webView.loadUrlNew(urls.getNotificationsUrl()); + openDiasporaUrl(urls.getNotificationsUrl()); return true; } else { snackbarNoInternet.show(); @@ -771,17 +604,7 @@ public class MainActivity extends AppCompatActivity case R.id.action_conversations: { if (WebHelper.isOnline(MainActivity.this)) { - webView.loadUrlNew(urls.getConversationsUrl()); - return true; - } else { - snackbarNoInternet.show(); - return false; - } - } - - case R.id.action_reload: { - if (WebHelper.isOnline(MainActivity.this)) { - webView.reload(); + openDiasporaUrl(urls.getConversationsUrl()); return true; } else { snackbarNoInternet.show(); @@ -795,47 +618,15 @@ public class MainActivity extends AppCompatActivity return true; } - case R.id.action_toggle_desktop_page: { - webView.loadUrlNew(urls.getToggleMobileUrl()); - return true; - } - case R.id.action_compose: { if (WebHelper.isOnline(MainActivity.this)) { - webView.loadUrlNew(urls.getNewPostUrl()); + openDiasporaUrl(urls.getNewPostUrl()); } else { snackbarNoInternet.show(); } return true; } - case R.id.action_go_to_top: { - // Scroll to top (animated) - ObjectAnimator anim = ObjectAnimator.ofInt(webView, "scrollY", webView.getScrollY(), 0); - anim.setDuration(400); - anim.start(); - return true; - } - - case R.id.action_share_link: { - Intent sharingIntent = new Intent(Intent.ACTION_SEND); - sharingIntent.setType("text/plain"); - sharingIntent.putExtra(Intent.EXTRA_SUBJECT, webView.getTitle()); - sharingIntent.putExtra(Intent.EXTRA_TEXT, webView.getUrl()); - startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.action_share_dotdotdot))); - return true; - } - - case R.id.action_take_screenshot: { - makeScreenshotOfWebView(false); - return true; - } - - case R.id.action_share_screenshot: { - makeScreenshotOfWebView(true); - return true; - } - case R.id.action_search: { if (WebHelper.isOnline(MainActivity.this)) { final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); @@ -847,9 +638,9 @@ public class MainActivity extends AppCompatActivity public void onClick(DialogInterface dialogInterface, int which) { String query = input.getText().toString().trim().replaceAll((which == DialogInterface.BUTTON_NEGATIVE ? "\\*" : "\\#"), ""); if (query.equals("")) { - Snackbar.make(contentLayout, R.string.search_alert_bypeople_validate_needsomedata, Snackbar.LENGTH_LONG).show(); + Snackbar.make(fragmentContainer, R.string.search_alert_bypeople_validate_needsomedata, Snackbar.LENGTH_LONG).show(); } else { - webView.loadUrl(which == DialogInterface.BUTTON_NEGATIVE ? urls.getSearchPeopleUrl(query) : urls.getSearchTagsUrl(query)); + openDiasporaUrl(which == DialogInterface.BUTTON_NEGATIVE ? urls.getSearchPeopleUrl(query) : urls.getSearchTagsUrl(query)); } getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); imm.hideSoftInputFromWindow(input.getWindowToken(), 0); @@ -890,100 +681,30 @@ public class MainActivity extends AppCompatActivity return super.onOptionsItemSelected(item); } - @SuppressWarnings("ResultOfMethodCallIgnored") - private boolean makeScreenshotOfWebView(boolean hasToShareScreenshot) { - AppLog.i(this, "makeScreenshotOfWebView()"); - if (android.os.Build.VERSION.SDK_INT >= 23) { - int hasWRITE_EXTERNAL_STORAGE = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE); - if (hasWRITE_EXTERNAL_STORAGE != PackageManager.PERMISSION_GRANTED) { - if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - new AlertDialog.Builder(MainActivity.this) - .setMessage(R.string.permissions_screenshot) - .setNegativeButton(android.R.string.no, null) - .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (android.os.Build.VERSION.SDK_INT >= 23) - requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, - REQUEST_CODE_ASK_PERMISSIONS); - } - }) - .show(); - return false; - } - requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, - REQUEST_CODE_ASK_PERMISSIONS); - return false; - } - } - - Date dateNow = new Date(); - DateFormat dateFormat = new SimpleDateFormat("yy_MM_dd--HH_mm_ss", Locale.getDefault()); - File fileSaveDirectory = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/Diaspora"); - - String fileSaveName = hasToShareScreenshot ? ".DfA_share.jpg" : String.format("DfA_%s.jpg", dateFormat.format(dateNow)); - if (!fileSaveDirectory.exists()) { - if (!fileSaveDirectory.mkdirs()) { - AppLog.w(this, "Could not mkdir " + fileSaveDirectory.getAbsolutePath()); - } - } - - if (!hasToShareScreenshot) { - Snackbar.make(contentLayout, getString(R.string.share__toast_screenshot) + " " + fileSaveName, Snackbar.LENGTH_LONG).show(); - } - - Bitmap bitmap; - webView.setDrawingCacheEnabled(true); - bitmap = Bitmap.createBitmap(webView.getDrawingCache()); - webView.setDrawingCacheEnabled(false); - - OutputStream bitmapWriter = null; - try { - bitmapWriter = new FileOutputStream(new File(fileSaveDirectory, fileSaveName)); - bitmap.compress(Bitmap.CompressFormat.JPEG, 85, bitmapWriter); - bitmapWriter.flush(); - bitmap.recycle(); - } catch (Exception e) { - return false; - } finally { - if (bitmapWriter != null) { - try { - bitmapWriter.close(); - } catch (IOException _ignSaveored) {/* Nothing */} - } - } - - // Only show share intent when Action Share Screenshot was selected - if (hasToShareScreenshot) { - Intent sharingIntent = new Intent(Intent.ACTION_SEND); - sharingIntent.setType("image/jpeg"); - sharingIntent.putExtra(Intent.EXTRA_SUBJECT, webView.getTitle()); - sharingIntent.putExtra(Intent.EXTRA_TEXT, webView.getUrl()); - Uri bmpUri = Uri.fromFile(new File(fileSaveDirectory, fileSaveName)); - sharingIntent.putExtra(Intent.EXTRA_STREAM, bmpUri); - startActivity(Intent.createChooser(sharingIntent, getString(R.string.action_share_dotdotdot))); - } else { - // Broadcast that this file is indexable - File file = new File(fileSaveDirectory, fileSaveName); - Uri uri = Uri.fromFile(file); - Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri); - sendBroadcast(intent); - } - return true; - } - + /** + * Update the profile name in the navigation slider + * @param name name + */ @Override public void onUserProfileNameChanged(String name) { AppLog.i(this, "onUserProfileNameChanged()"); navheaderTitle.setText(name); } + /** + * Update the profile picture in the navigation slider + * @param avatarUrl url of the new profile pic + */ @Override public void onUserProfileAvatarChanged(String avatarUrl) { AppLog.i(this, "onUserProfileAvatarChanged()"); app.getAvatarImageLoader().startImageDownload(navheaderImage, avatarUrl); } + /** + * Handle hashtag clicks. Open the new-post-url and inject the clicked hashtag into the post-editor + * @param intent intent + */ private void handleHashtag(Intent intent) { AppLog.v(this, "handleHashtag()"); try { @@ -991,18 +712,21 @@ public class MainActivity extends AppCompatActivity } catch (Exception e) { AppLog.e(this, e.toString()); } - webView.loadUrlNew(urls.getNewPostUrl()); + openDiasporaUrl(urls.getNewPostUrl()); } + /** + * Open the new-post-url and inject text that was shared into the app into the post editors text field + * @param intent shareTextIntent + */ private void handleSendText(Intent intent) { AppLog.v(this, "handleSendText()"); try { setSharedTexts(null, intent.getStringExtra(Intent.EXTRA_TEXT)); + openDiasporaUrl(urls.getNewPostUrl()); } catch (Exception e) { AppLog.e(this, e.toString()); } - webView.loadUrlNew(urls.getBlankUrl()); - webView.loadUrlNew(urls.getNewPostUrl()); } /** @@ -1014,14 +738,14 @@ public class MainActivity extends AppCompatActivity AppLog.v(this, "handleSendSubject()"); try { setSharedTexts(intent.getStringExtra(Intent.EXTRA_SUBJECT), intent.getStringExtra(Intent.EXTRA_TEXT)); + openDiasporaUrl(urls.getNewPostUrl()); } catch (Exception e) { AppLog.e(this, e.toString()); } - webView.loadUrlNew(urls.getBlankUrl()); //TODO: Necessary? - webView.loadUrlNew(urls.getNewPostUrl()); } /** + * TODO: MOVE * Set sharedText variable to escaped and formatted subject + body. * If subject is null, only the body will be set. Else the subject will be set as header. * Depending on whether the user has the setting isAppendSharedViaApp set, a reference to @@ -1041,29 +765,34 @@ public class MainActivity extends AppCompatActivity if (sharedSubject != null) { AppLog.v(this, "Append subject to shared text"); String escapedSubject = WebHelper.escapeHtmlText(WebHelper.replaceUrlWithMarkdown(sharedSubject)); + AppLog.v(this, "Set shared text; Subject: \"" + escapedSubject + "\" Body: \"" + escapedBody + "\""); textToBeShared = "**" + escapedSubject + "** " + escapedBody; } else { AppLog.v(this, "Set shared text; Subject: \"" + sharedSubject + "\" Body: \"" + sharedBody + "\""); textToBeShared = escapedBody; } - - } - //TODO: Implement? + /** + * Share an image shared to the app via diaspora + * @param intent shareImageIntent + */ + //TODO: Implement some day private void handleSendImage(Intent intent) { AppLog.i(this, "handleSendImage()"); final Uri imageUri = intent.getParcelableExtra(Intent.EXTRA_STREAM); if (imageUri != null) { AppLog.v(this, "imageUri is not null. Handle shared image"); - // TODO: Update UI to reflect text being shared } else { AppLog.w(this, "imageUri is null. Cannot precede."); } Toast.makeText(this, "Not yet implemented.", Toast.LENGTH_SHORT).show(); } - // TODO: Move from Javascript interface + /** + * Invalidate the top toolbar to update the notification counter + * @param notificationCount new notification count + */ @Override public void onNotificationCountChanged(int notificationCount) { AppLog.i(this, "onNotificationCountChanged()"); @@ -1071,7 +800,10 @@ public class MainActivity extends AppCompatActivity invalidateOptionsMenu(); } - // TODO: Move from Javascript interface + /** + * Invalidate the top toolbar to update the unread messages counter + * @param unreadMessageCount new unread messages count + */ @Override public void onUnreadMessageCountChanged(int unreadMessageCount) { AppLog.i(this, "onUnreadMessageCountChanged()"); @@ -1079,22 +811,17 @@ public class MainActivity extends AppCompatActivity invalidateOptionsMenu(); } - private class JavaScriptInterface { - @JavascriptInterface - public void setUserProfile(final String webMessage) throws JSONException { - AppLog.spam(this, "JavaScriptInterface.setUserProfile()"); - if (podUserProfile.isRefreshNeeded()) { - AppLog.spam(this, "PodUserProfile needs refresh; Try to parse JSON"); - podUserProfile.parseJson(webMessage); - } else { - AppLog.spam(this, "No PodUserProfile refresh needed"); - } + @Override + public void onCustomTabsConnected() { + if(customTabsSession == null) { + AppLog.i(this, "CustomTabs warmup: "+customTabActivityHelper.warmup(0)); + customTabsSession = customTabActivityHelper.getSession(); } + } + + @Override + public void onCustomTabsDisconnected() { - @JavascriptInterface - public void contentHasBeenShared() { - textToBeShared = null; - } } @SuppressWarnings("StatementWithEmptyBody") @@ -1105,7 +832,7 @@ public class MainActivity extends AppCompatActivity switch (item.getItemId()) { case R.id.nav_stream: { if (WebHelper.isOnline(MainActivity.this)) { - webView.loadUrlNew(urls.getStreamUrl()); + openDiasporaUrl(urls.getStreamUrl()); } else { snackbarNoInternet.show(); } @@ -1114,7 +841,7 @@ public class MainActivity extends AppCompatActivity case R.id.nav_profile: { if (WebHelper.isOnline(MainActivity.this)) { - webView.loadUrlNew(urls.getProfileUrl()); + openDiasporaUrl(urls.getProfileUrl()); } else { snackbarNoInternet.show(); } @@ -1122,19 +849,16 @@ public class MainActivity extends AppCompatActivity break; case R.id.nav_followed_tags: { - if (WebHelper.isOnline(MainActivity.this)) { - WebHelper.showFollowedTagsList(webView, app); - setTitle(R.string.nav_followed_tags); - } else { - snackbarNoInternet.show(); - } + showFragment(getFragment(HashtagListFragment.TAG)); } break; + //TODO: Replace with fragment case R.id.nav_aspects: { + DiasporaStreamFragment stream = (DiasporaStreamFragment) getFragment(DiasporaStreamFragment.TAG); if (WebHelper.isOnline(MainActivity.this)) { - webView.loadUrlNew(DiasporaUrlHelper.URL_BLANK); - WebHelper.showAspectList(webView, app); + openDiasporaUrl(DiasporaUrlHelper.URL_BLANK); + WebHelper.showAspectList(stream.getWebView(), app); setTitle(R.string.aspects); } else { snackbarNoInternet.show(); @@ -1144,7 +868,7 @@ public class MainActivity extends AppCompatActivity case R.id.nav_activities: { if (WebHelper.isOnline(MainActivity.this)) { - webView.loadUrlNew(urls.getActivityUrl()); + openDiasporaUrl(urls.getActivityUrl()); } else { snackbarNoInternet.show(); } @@ -1153,7 +877,7 @@ public class MainActivity extends AppCompatActivity case R.id.nav_liked: { if (WebHelper.isOnline(MainActivity.this)) { - webView.loadUrlNew(urls.getLikedPostsUrl()); + openDiasporaUrl(urls.getLikedPostsUrl()); } else { snackbarNoInternet.show(); } @@ -1162,7 +886,7 @@ public class MainActivity extends AppCompatActivity case R.id.nav_commented: { if (WebHelper.isOnline(MainActivity.this)) { - webView.loadUrlNew(urls.getCommentedUrl()); + openDiasporaUrl(urls.getCommentedUrl()); } else { snackbarNoInternet.show(); } @@ -1171,7 +895,7 @@ public class MainActivity extends AppCompatActivity case R.id.nav_mentions: { if (WebHelper.isOnline(MainActivity.this)) { - webView.loadUrlNew(urls.getMentionsUrl()); + openDiasporaUrl(urls.getMentionsUrl()); } else { snackbarNoInternet.show(); } @@ -1180,7 +904,7 @@ public class MainActivity extends AppCompatActivity case R.id.nav_public: { if (WebHelper.isOnline(MainActivity.this)) { - webView.loadUrlNew(urls.getPublicUrl()); + openDiasporaUrl(urls.getPublicUrl()); } else { snackbarNoInternet.show(); } @@ -1198,7 +922,7 @@ public class MainActivity extends AppCompatActivity } break; - case R.id.nav_help_license: { + case R.id.nav_about: { startActivity(new Intent(MainActivity.this, AboutActivity.class)); } break; @@ -1208,6 +932,12 @@ public class MainActivity extends AppCompatActivity return true; } + /** + * React to results of requestPermission + * @param requestCode resCode + * @param permissions requested permissions + * @param grantResults granted results + */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { @@ -1228,81 +958,18 @@ public class MainActivity extends AppCompatActivity } /** - * Set proxy according to arguments. host must not be "" or null, port must be positive. - * Return true on success and update appSettings' proxy related values. - * - * @param host proxy host (eg. localhost or 127.0.0.1) - * @param port proxy port (eg. 8118) - * @return success - * @throws IllegalArgumentException if arguments do not fit specifications above + * Return the string that will be shared into the new-post-editor + * @return String */ - private boolean setProxy(final String host, final int port) { - AppLog.v(this, "setProxy()"); - if (host != null && !host.equals("") && port >= 0) { - AppLog.v(this, "Set proxy to " + host + ":" + port); - //Temporary change thread policy - AppLog.v(this, "Set temporary ThreadPolicy"); - StrictMode.ThreadPolicy old = StrictMode.getThreadPolicy(); - StrictMode.ThreadPolicy tmp = new StrictMode.ThreadPolicy.Builder().permitAll().build(); - StrictMode.setThreadPolicy(tmp); - - AppLog.v(this, "Apply NetCipher proxy settings"); - NetCipher.setProxy(host, port); //Proxy for HttpsUrlConnections - try { - //Proxy for the ui__webview - AppLog.v(this, "Apply Webkit proxy settings"); - WebkitProxy.setProxy(MainActivity.class.getName(), getApplicationContext(), null, host, port); - } catch (Exception e) { - AppLog.e(this, "Could not apply WebKit proxy settings:\n" + e.toString()); - } - AppLog.v(this, "Save changes in appSettings"); - appSettings.setProxyEnabled(true); - appSettings.setProxyWasEnabled(true); - - AppLog.v(this, "Reset old ThreadPolicy"); - StrictMode.setThreadPolicy(old); - AppLog.v(this, "Success! Reload WebView"); - webView.reload(); - return true; - } else { - AppLog.w(this, "Invalid proxy configuration. Host: " + host + " Port: " + port + "\nRefuse to set proxy"); - return false; - } + public String getTextToBeShared() { + return textToBeShared; } - private boolean setProxy() { - return setProxy(appSettings.getProxyHost(), appSettings.getProxyPort()); - } - - private void resetProxy() { - AppLog.i(this, "resetProxy()"); - AppLog.v(this, "write changes to appSettings"); - appSettings.setProxyEnabled(false); - appSettings.setProxyWasEnabled(false); - - //Temporary change thread policy - AppLog.v(this, "Set temporary ThreadPolicy"); - StrictMode.ThreadPolicy old = StrictMode.getThreadPolicy(); - StrictMode.ThreadPolicy tmp = new StrictMode.ThreadPolicy.Builder().permitAll().build(); - StrictMode.setThreadPolicy(tmp); - - AppLog.v(this, "clear NetCipher proxy"); - NetCipher.clearProxy(); - try { - AppLog.v(this, "clear WebKit proxy"); - WebkitProxy.resetProxy(MainActivity.class.getName(), this); - } catch (Exception e) { - AppLog.e(this, "Could not clear WebKit proxy:\n" + e.toString()); - } - AppLog.v(this, "Reset old ThreadPolicy"); - StrictMode.setThreadPolicy(old); - - //Restart app - AppLog.i(this, "Success! Restart app due to proxy reset"); - Intent restartActivity = new Intent(this, MainActivity.class); - PendingIntent pendingIntent = PendingIntent.getActivity(this, 12374, restartActivity, PendingIntent.FLAG_CANCEL_CURRENT); - AlarmManager mgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE); - mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendingIntent); - System.exit(0); + /** + * Set the string that will be shared into the new-post-editor + * @param textToBeShared + */ + public void setTextToBeShared(String textToBeShared) { + this.textToBeShared = textToBeShared; } } diff --git a/app/src/main/java/com/github/dfa/diaspora_android/activity/SettingsActivity.java b/app/src/main/java/com/github/dfa/diaspora_android/activity/SettingsActivity.java index a1af7b9d..af1f3860 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/activity/SettingsActivity.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/activity/SettingsActivity.java @@ -94,8 +94,7 @@ public class SettingsActivity extends AppCompatActivity { @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { updatePreference(findPreference(key)); - if (key != null && isAdded() && (key.equals(getString(R.string.pref_key__clear_cache)) || - key.equals(getString(R.string.pref_key__font_size)) || + if (key != null && isAdded() && (key.equals(getString(R.string.pref_key__font_size)) || key.equals(getString(R.string.pref_key__load_images)) || key.equals(getString(R.string.pref_key__intellihide_toolbars)) || key.equals(getString(R.string.pref_key__http_proxy_enabled)) || @@ -162,11 +161,17 @@ public class SettingsActivity extends AppCompatActivity { return true; } case R.string.pref_title__http_proxy_load_tor_preset: { - ((EditTextPreference)findPreference(getString(R.string.pref_key__http_proxy_host))).setText("127.0.0.1"); - ((EditTextPreference)findPreference(getString(R.string.pref_key__http_proxy_port))).setText("8118"); + ((EditTextPreference) findPreference(getString(R.string.pref_key__http_proxy_host))).setText("127.0.0.1"); + ((EditTextPreference) findPreference(getString(R.string.pref_key__http_proxy_port))).setText("8118"); return true; } + case R.string.pref_title__clear_cache: + { + intent.setAction(MainActivity.ACTION_CLEAR_CACHE); + break; + } + default: { intent = null; break; diff --git a/app/src/main/java/com/github/dfa/diaspora_android/activity/SplashActivity.java b/app/src/main/java/com/github/dfa/diaspora_android/activity/SplashActivity.java deleted file mode 100644 index 0eeccd3c..00000000 --- a/app/src/main/java/com/github/dfa/diaspora_android/activity/SplashActivity.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - This file is part of the Diaspora for Android. - - Diaspora for Android is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Diaspora for Android is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with the Diaspora for Android. - - If not, see . - */ - -package com.github.dfa.diaspora_android.activity; - -import android.os.Bundle; -import android.os.Handler; -import android.support.v7.app.AppCompatActivity; - -import com.github.dfa.diaspora_android.App; -import com.github.dfa.diaspora_android.R; -import com.github.dfa.diaspora_android.util.Helpers; - -import butterknife.ButterKnife; - - -public class SplashActivity extends AppCompatActivity { - private App app; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.splash__activity); - ButterKnife.bind(this); - app = (App) getApplication(); - - int delay = getResources().getInteger(R.integer.splash_delay); - new Handler().postDelayed(startActivityRunnable, delay); - } - - private final Runnable startActivityRunnable = new Runnable() { - public void run() { - boolean hasPodDomain = app.getSettings().hasPodDomain(); - Helpers.animateToActivity(SplashActivity.this, - hasPodDomain ? MainActivity.class : PodSelectionActivity.class, - true - ); - } - }; -} diff --git a/app/src/main/java/com/github/dfa/diaspora_android/fragment/BrowserFragment.java b/app/src/main/java/com/github/dfa/diaspora_android/fragment/BrowserFragment.java new file mode 100644 index 00000000..70ee1054 --- /dev/null +++ b/app/src/main/java/com/github/dfa/diaspora_android/fragment/BrowserFragment.java @@ -0,0 +1,371 @@ +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ +package com.github.dfa.diaspora_android.fragment; + +import android.Manifest; +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Bundle; +import android.os.Environment; +import android.os.StrictMode; +import android.support.design.widget.Snackbar; +import android.support.v7.app.AlertDialog; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.widget.ProgressBar; +import android.widget.Toast; + +import com.github.dfa.diaspora_android.App; +import com.github.dfa.diaspora_android.R; +import com.github.dfa.diaspora_android.activity.MainActivity; +import com.github.dfa.diaspora_android.data.AppSettings; +import com.github.dfa.diaspora_android.ui.ContextMenuWebView; +import com.github.dfa.diaspora_android.webview.CustomWebViewClient; +import com.github.dfa.diaspora_android.webview.ProgressBarWebChromeClient; +import com.github.dfa.diaspora_android.util.AppLog; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +import info.guardianproject.netcipher.NetCipher; +import info.guardianproject.netcipher.webkit.WebkitProxy; + +/** + * Fragment with a webView and a ProgressBar. + * This Fragment retains its instance. + * Created by vanitas on 26.09.16. + */ + +public class BrowserFragment extends CustomFragment { + public static final String TAG = "com.github.dfa.diaspora_android.BrowserFragment"; + + protected View rootLayout; + protected ContextMenuWebView webView; + protected ProgressBar progressBar; + protected AppSettings appSettings; + protected CustomWebViewClient webViewClient; + protected WebSettings webSettings; + + protected String pendingUrl; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + AppLog.d(this, "onCreateView()"); + if(rootLayout == null) { + rootLayout = inflater.inflate(R.layout.browser__fragment, container, false); + } + return rootLayout; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + AppLog.d(this, "onViewCreated()"); + super.onViewCreated(view, savedInstanceState); + + if(this.appSettings == null) { + this.appSettings = ((App) getActivity().getApplication()).getSettings(); + } + + if(this.webView == null) { + this.webView = (ContextMenuWebView) view.findViewById(R.id.webView); + this.applyWebViewSettings(); + } + + if(this.progressBar == null) { + this.progressBar = (ProgressBar) view.findViewById(R.id.progressBar); + } + + if (appSettings.isProxyEnabled()) { + if (!setProxy(appSettings.getProxyHost(), appSettings.getProxyPort())) { + AppLog.e(this, "Could not enable Proxy"); + Toast.makeText(getContext(), R.string.toast_set_proxy_failed, Toast.LENGTH_SHORT).show(); + } + } else if (appSettings.wasProxyEnabled()) { + resetProxy(); + } + + if(pendingUrl != null) { + loadUrl(pendingUrl); + pendingUrl = null; + } + + webView.setParentActivity(getActivity()); + + this.setRetainInstance(true); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + if (getRetainInstance() && rootLayout.getParent() instanceof ViewGroup) { + ((ViewGroup) rootLayout.getParent()).removeView(rootLayout); + } + } + + private void applyWebViewSettings() { + this.webSettings = webView.getSettings(); + webSettings.setAllowFileAccess(false); + webSettings.setUseWideViewPort(true); + webSettings.setLoadWithOverviewMode(true); + webSettings.setDomStorageEnabled(true); + webSettings.setMinimumFontSize(appSettings.getMinimumFontSize()); + webSettings.setLoadsImagesAutomatically(appSettings.isLoadImages()); + webSettings.setAppCacheEnabled(true); + + if (android.os.Build.VERSION.SDK_INT >= 21) { + WebView.enableSlowWholeDocumentDraw(); + webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); + } + + this.registerForContextMenu(webView); + //webView.setParentActivity(this); + webView.setOverScrollMode(WebView.OVER_SCROLL_ALWAYS); + + this.webViewClient = new CustomWebViewClient((App) getActivity().getApplication(), webView); + webView.setWebViewClient(webViewClient); + webView.setWebChromeClient(new ProgressBarWebChromeClient(webView, progressBar)); + } + + /** + * Set proxy according to arguments. host must not be "" or null, port must be positive. + * Return true on success and update appSettings' proxy related values. + * + * @param host proxy host (eg. localhost or 127.0.0.1) + * @param port proxy port (eg. 8118) + * @return success + * @throws IllegalArgumentException if arguments do not fit specifications above + */ + private boolean setProxy(final String host, final int port) { + AppLog.i(this, "StreamFragment.setProxy()"); + if (host != null && !host.equals("") && port >= 0) { + AppLog.i(this, "Set proxy to "+host+":"+port); + //Temporary change thread policy + AppLog.v(this, "Set temporary ThreadPolicy"); + StrictMode.ThreadPolicy old = StrictMode.getThreadPolicy(); + StrictMode.ThreadPolicy tmp = new StrictMode.ThreadPolicy.Builder().permitAll().build(); + StrictMode.setThreadPolicy(tmp); + + AppLog.v(this, "Apply NetCipher proxy settings"); + NetCipher.setProxy(host, port); //Proxy for HttpsUrlConnections + try { + //Proxy for the webview + AppLog.v(this, "Apply Webkit proxy settings"); + WebkitProxy.setProxy(MainActivity.class.getName(), getContext().getApplicationContext(), null, host, port); + } catch (Exception e) { + AppLog.e(this, "Could not apply WebKit proxy settings:\n"+e.toString()); + } + AppLog.v(this, "Save changes in appSettings"); + appSettings.setProxyEnabled(true); + appSettings.setProxyWasEnabled(true); + + AppLog.v(this, "Reset old ThreadPolicy"); + StrictMode.setThreadPolicy(old); + AppLog.i(this, "Success! Reload WebView"); + webView.reload(); + return true; + } else { + AppLog.e(this, "Invalid proxy configuration. Host: "+host+" Port: "+port+"\nRefuse to set proxy"); + return false; + } + } + + @SuppressWarnings("unused") + private boolean setProxy() { + return setProxy(appSettings.getProxyHost(), appSettings.getProxyPort()); + } + + private void resetProxy() { + AppLog.i(this, "StreamFragment.resetProxy()"); + AppLog.v(this, "write changes to appSettings"); + appSettings.setProxyEnabled(false); + appSettings.setProxyWasEnabled(false); + + //Temporary change thread policy + AppLog.v(this, "Set temporary ThreadPolicy"); + StrictMode.ThreadPolicy old = StrictMode.getThreadPolicy(); + StrictMode.ThreadPolicy tmp = new StrictMode.ThreadPolicy.Builder().permitAll().build(); + StrictMode.setThreadPolicy(tmp); + + AppLog.v(this, "clear NetCipher proxy"); + NetCipher.clearProxy(); + try { + AppLog.v(this, "clear WebKit proxy"); + WebkitProxy.resetProxy(MainActivity.class.getName(), getContext()); + } catch (Exception e) { + AppLog.e(this, "Could not clear WebKit proxy:\n"+e.toString()); + } + AppLog.v(this, "Reset old ThreadPolicy"); + StrictMode.setThreadPolicy(old); + + //Restart app + AppLog.i(this, "Success! Restart app due to proxy reset"); + Intent restartActivity = new Intent(getContext(), MainActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), 12374, restartActivity, PendingIntent.FLAG_CANCEL_CURRENT); + AlarmManager mgr = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE); + mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendingIntent); + System.exit(0); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + protected boolean makeScreenshotOfWebView(boolean hasToShareScreenshot) { + AppLog.i(this, "StreamFragment.makeScreenshotOfWebView()"); + if (android.os.Build.VERSION.SDK_INT >= 23) { + int hasWRITE_EXTERNAL_STORAGE = getActivity().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE); + if (hasWRITE_EXTERNAL_STORAGE != PackageManager.PERMISSION_GRANTED) { + if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + new AlertDialog.Builder(getContext()) + .setMessage(R.string.permissions_screenshot) + .setNegativeButton(android.R.string.no, null) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (android.os.Build.VERSION.SDK_INT >= 23) + requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + MainActivity.REQUEST_CODE_ASK_PERMISSIONS); + } + }) + .show(); + return false; + } + requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + MainActivity.REQUEST_CODE_ASK_PERMISSIONS); + return false; + } + } + + Date dateNow = new Date(); + DateFormat dateFormat = new SimpleDateFormat("yy_MM_dd--HH_mm_ss", Locale.getDefault()); + File fileSaveDirectory = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/Diaspora"); + + String fileSaveName = hasToShareScreenshot ? ".DfA_share.jpg" : String.format("DfA_%s.jpg", dateFormat.format(dateNow)); + if (!fileSaveDirectory.exists()) { + if(!fileSaveDirectory.mkdirs()) { + AppLog.w(this, "Could not mkdir "+fileSaveDirectory.getAbsolutePath()); + } + } + + if (!hasToShareScreenshot) { + Snackbar.make(webView, getString(R.string.share__toast_screenshot) + " " + fileSaveName, Snackbar.LENGTH_LONG).show(); + } + + Bitmap bitmap; + webView.setDrawingCacheEnabled(true); + bitmap = Bitmap.createBitmap(webView.getDrawingCache()); + webView.setDrawingCacheEnabled(false); + + OutputStream bitmapWriter = null; + try { + bitmapWriter = new FileOutputStream(new File(fileSaveDirectory, fileSaveName)); + bitmap.compress(Bitmap.CompressFormat.JPEG, 85, bitmapWriter); + bitmapWriter.flush(); + bitmap.recycle(); + } catch (Exception e) { + return false; + } finally { + if (bitmapWriter != null) { + try { + bitmapWriter.close(); + } catch (IOException _ignSaveored) {/* Nothing */} + } + } + + // Only show share intent when Action Share Screenshot was selected + if (hasToShareScreenshot) { + Intent sharingIntent = new Intent(Intent.ACTION_SEND); + sharingIntent.setType("image/jpeg"); + sharingIntent.putExtra(Intent.EXTRA_SUBJECT, webView.getTitle()); + sharingIntent.putExtra(Intent.EXTRA_TEXT, webView.getUrl()); + Uri bmpUri = Uri.fromFile(new File(fileSaveDirectory, fileSaveName)); + sharingIntent.putExtra(Intent.EXTRA_STREAM, bmpUri); + startActivity(Intent.createChooser(sharingIntent, getString(R.string.action_share_dotdotdot))); + } else { + // Broadcast that this file is indexable + File file = new File(fileSaveDirectory, fileSaveName); + Uri uri = Uri.fromFile(file); + Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri); + getActivity().sendBroadcast(intent); + } + return true; + } + + @Override + public String getFragmentTag() { + return TAG; + } + + @Override + public void onCreateBottomOptionsMenu(Menu menu, MenuInflater inflater) { + /* Nothing to do here */ + } + + public boolean onBackPressed() { + if(webView.canGoBack()) { + webView.goBack(); + return true; + } + return false; + } + + public void loadUrl(String url) { + if(getWebView() != null) { + AppLog.v(this, "loadUrl(): load "+url); + getWebView().loadUrlNew(url); + } else { + AppLog.v(this, "loadUrl(): WebView null: Set pending url to "+url); + pendingUrl = url; + } + } + + public String getUrl() { + if(getWebView() != null) { + return getWebView().getUrl(); + } else { + return pendingUrl; + } + } + + public void reloadUrl() { + AppLog.v(this, "reloadUrl()"); + if(getWebView() != null) { + getWebView().reload(); + } + } + + public ContextMenuWebView getWebView() { + return this.webView; + } +} diff --git a/app/src/main/java/com/github/dfa/diaspora_android/fragment/CustomFragment.java b/app/src/main/java/com/github/dfa/diaspora_android/fragment/CustomFragment.java new file mode 100644 index 00000000..3673d739 --- /dev/null +++ b/app/src/main/java/com/github/dfa/diaspora_android/fragment/CustomFragment.java @@ -0,0 +1,65 @@ +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ +package com.github.dfa.diaspora_android.fragment; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.Menu; +import android.view.MenuInflater; + +/** + * Customized abstract Fragment class with some useful methods + * Created by vanitas on 21.09.16. + */ + +public abstract class CustomFragment extends Fragment { + + public static final String TAG = "com.github.dfa.diaspora_android.CustomFragment"; + + /** + * We have an optionsMenu + * @param savedInstanceState state + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + /** + * Return the tag used to identify the Fragment. + * @return tag + */ + public abstract String getFragmentTag(); + + /** + * Add fragment-dependent options to the bottom options toolbar + * @param menu bottom menu + * @param inflater inflater + */ + public abstract void onCreateBottomOptionsMenu(Menu menu, MenuInflater inflater); + + /** + * Return true if the fragment reacted to a back button press, false else. + * In case the fragment returned false, the parent activity should handle the backPress. + * @return did we react to the back press? + */ + public abstract boolean onBackPressed(); +} + diff --git a/app/src/main/java/com/github/dfa/diaspora_android/fragment/DiasporaStreamFragment.java b/app/src/main/java/com/github/dfa/diaspora_android/fragment/DiasporaStreamFragment.java new file mode 100644 index 00000000..d10b4e6d --- /dev/null +++ b/app/src/main/java/com/github/dfa/diaspora_android/fragment/DiasporaStreamFragment.java @@ -0,0 +1,345 @@ +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ +package com.github.dfa.diaspora_android.fragment; + +import android.Manifest; +import android.animation.ObjectAnimator; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.provider.MediaStore; +import android.support.design.widget.Snackbar; +import android.support.v7.app.AlertDialog; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.webkit.JavascriptInterface; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient; +import android.webkit.WebView; + +import com.github.dfa.diaspora_android.App; +import com.github.dfa.diaspora_android.R; +import com.github.dfa.diaspora_android.activity.MainActivity; +import com.github.dfa.diaspora_android.data.PodUserProfile; +import com.github.dfa.diaspora_android.webview.DiasporaStreamWebChromeClient; +import com.github.dfa.diaspora_android.webview.FileUploadWebChromeClient; +import com.github.dfa.diaspora_android.util.AppLog; +import com.github.dfa.diaspora_android.util.DiasporaUrlHelper; +import com.github.dfa.diaspora_android.util.Helpers; +import com.github.dfa.diaspora_android.util.WebHelper; + +import org.json.JSONException; + +import java.io.File; +import java.io.IOException; + +/** + * Fragment that displays the Stream of the diaspora* user + * Created by vanitas on 26.09.16. + */ + +public class DiasporaStreamFragment extends BrowserFragment { + public static final String TAG = "com.github.dfa.diaspora_android.StreamFragment"; + + protected DiasporaUrlHelper urls; + + private ValueCallback imageUploadFilePathCallbackNew; + private ValueCallback imageUploadFilePathCallbackOld; + private String mCameraPhotoPath; + + @SuppressLint("SetJavaScriptEnabled") + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + this.urls = new DiasporaUrlHelper(appSettings); + webView.setWebChromeClient(new DiasporaStreamWebChromeClient(webView, progressBar, fileUploadCallback, sharedTextCallback)); + + webView.getSettings().setJavaScriptEnabled(true); + webView.addJavascriptInterface(new JavaScriptInterface(), "AndroidBridge"); + if(((MainActivity)getActivity()).getTextToBeShared() != null) { + loadUrl(urls.getNewPostUrl()); + } else if(webView.getUrl() == null) { + loadUrl(urls.getStreamUrl()); + } + } + + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.stream__menu_top, menu); + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public void onCreateBottomOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.stream__menu_bottom, menu); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + AppLog.d(this, "onActivityResult(): "+requestCode); + switch (requestCode) { + case MainActivity.INPUT_FILE_REQUEST_CODE_NEW: + case MainActivity.INPUT_FILE_REQUEST_CODE_OLD: + AppLog.v(this, "INPUT_FILE_REQUEST_CODE: "+requestCode); + onImageUploadResult(requestCode, resultCode, data); + return; + } + super.onActivityResult(requestCode, resultCode, data); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + AppLog.d(this, "StreamFragment.onOptionsItemSelected()"); + switch (item.getItemId()) { + case R.id.action_reload: { + if(WebHelper.isOnline(getContext())) { + reloadUrl(); + return true; + } else { + return false; + } + } + + case R.id.action_toggle_desktop_page: { + loadUrl(urls.getToggleMobileUrl()); + return true; + } + + case R.id.action_go_to_top: { + ObjectAnimator anim = ObjectAnimator.ofInt(webView, "scrollY", webView.getScrollY(), 0); + anim.setDuration(400); + anim.start(); + return true; + } + + case R.id.action_share_link: { + Intent sharingIntent = new Intent(Intent.ACTION_SEND); + sharingIntent.setType("text/plain"); + sharingIntent.putExtra(Intent.EXTRA_SUBJECT, webView.getTitle()); + sharingIntent.putExtra(Intent.EXTRA_TEXT, webView.getUrl()); + startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.action_share_dotdotdot))); + return true; + } + + case R.id.action_take_screenshot: { + makeScreenshotOfWebView(false); + return true; + } + + case R.id.action_share_screenshot: { + makeScreenshotOfWebView(true); + return true; + } + } + return super.onOptionsItemSelected(item); + } + + public void onImageUploadResult(int requestCode, int resultCode, Intent data) { + AppLog.d(this, "onImageUploadResult"); + switch (requestCode) { + case MainActivity.INPUT_FILE_REQUEST_CODE_NEW: { + AppLog.v(this, "Upload image using recent method (Lollipop+)"); + if (imageUploadFilePathCallbackNew == null || resultCode != Activity.RESULT_OK) { + AppLog.e(this, "Callback is null: " + (imageUploadFilePathCallbackNew == null) + + " resultCode: " + resultCode); + if (imageUploadFilePathCallbackNew != null) + imageUploadFilePathCallbackNew.onReceiveValue(new Uri[]{}); + return; + } + Uri[] results = null; + if (data == null) { + if (mCameraPhotoPath != null) { + AppLog.v(this, "Intent data is null. Try to parse cameraPhotoPath"); + results = new Uri[]{Uri.parse(mCameraPhotoPath)}; + } else { + AppLog.w(this, "Intent data is null and cameraPhotoPath is null"); + } + } else { + String dataString = data.getDataString(); + if (dataString != null) { + AppLog.v(this, "Intent has data. Try to parse dataString"); + results = new Uri[]{Uri.parse(dataString)}; + } else { + AppLog.w(this, "dataString is null"); + } + } + AppLog.v(this, "handle received result over to callback"); + imageUploadFilePathCallbackNew.onReceiveValue(results); + imageUploadFilePathCallbackNew = null; + return; + } + case MainActivity.INPUT_FILE_REQUEST_CODE_OLD: { + AppLog.v(this, "Upload image using legacy method (Jelly Bean, Kitkat)"); + if (imageUploadFilePathCallbackOld == null || resultCode != Activity.RESULT_OK) { + AppLog.e(this, "Callback is null: " + (imageUploadFilePathCallbackOld == null) + + " resultCode: " + resultCode); + if (imageUploadFilePathCallbackOld != null) + imageUploadFilePathCallbackOld.onReceiveValue(null); + return; + } + Uri results = null; + if (data == null) { + if (mCameraPhotoPath != null) { + AppLog.v(this, "Intent has no data. Try to parse cameraPhotoPath"); + results = Uri.parse(mCameraPhotoPath); + } else { + AppLog.w(this, "Intent has no data and cameraPhotoPath is null"); + } + } else { + String dataString = data.getDataString(); + if (dataString != null) { + AppLog.v(this, "Intent has data. Try to parse dataString"); + results = Uri.parse(dataString); + } else { + AppLog.w(this, "dataString is null"); + } + } + AppLog.v(this, "handle received result over to callback"); + imageUploadFilePathCallbackOld.onReceiveValue(results); + imageUploadFilePathCallbackOld = null; + } + } + } + + protected DiasporaStreamWebChromeClient.SharedTextCallback sharedTextCallback = new DiasporaStreamWebChromeClient.SharedTextCallback() { + @Override + public String getSharedText() { + return ((MainActivity)getActivity()).getTextToBeShared(); + } + @Override + public void setSharedText(String shared) { + ((MainActivity)getActivity()).setTextToBeShared(shared); + } + }; + + protected FileUploadWebChromeClient.FileUploadCallback fileUploadCallback = new FileUploadWebChromeClient.FileUploadCallback() { + @Override + public boolean imageUpload(WebView webView, ValueCallback filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) { + if(Build.VERSION.SDK_INT >= 23) { + int hasWRITE_EXTERNAL_STORAGE = getActivity().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE); + if (hasWRITE_EXTERNAL_STORAGE != PackageManager.PERMISSION_GRANTED) { + if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + new AlertDialog.Builder(getContext()) + .setMessage(R.string.permissions_image) + .setNegativeButton(android.R.string.no, null) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (android.os.Build.VERSION.SDK_INT >= 23) + requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + MainActivity.REQUEST_CODE_ASK_PERMISSIONS); + } + }) + .show(); + return false; + } + requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + MainActivity.REQUEST_CODE_ASK_PERMISSIONS); + return false; + } + } + AppLog.v(this, "onOpenFileChooser"); + if (imageUploadFilePathCallbackNew != null) imageUploadFilePathCallbackNew.onReceiveValue(null); + imageUploadFilePathCallbackNew = filePathCallback; + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + if (takePictureIntent.resolveActivity(getContext().getPackageManager()) != null) { + // Create the File where the photo should go + File photoFile; + try { + photoFile = Helpers.createImageFile(); + takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath); + } catch (IOException ex) { + AppLog.e(this, "ERROR creating temp file: "+ ex.toString()); + // Error occurred while creating the File + Snackbar.make(webView, R.string.unable_to_load_image, Snackbar.LENGTH_LONG).show(); + return false; + } + // Continue only if the File was successfully created + if (photoFile != null) { + mCameraPhotoPath = "file:" + photoFile.getAbsolutePath(); + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, + Uri.fromFile(photoFile)); + } else { + takePictureIntent = null; + } + } + Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT); + contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE); + contentSelectionIntent.setType("image/*"); + Intent[] intentArray; + if (takePictureIntent != null) { + intentArray = new Intent[]{takePictureIntent}; + } else { + intentArray = new Intent[0]; + } + Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER); + chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent); + chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser"); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); + AppLog.d(this, "startActivityForResult"); + startActivityForResult(chooserIntent, MainActivity.INPUT_FILE_REQUEST_CODE_NEW); + return true; + } + + @Override + public void legacyImageUpload(ValueCallback uploadMsg, String acceptType, String capture) { + AppLog.v(this, "openFileChooser(ValCallback, String, String"); + imageUploadFilePathCallbackOld = uploadMsg; + Intent intent = new Intent(); + intent.setType("image/*"); + intent.setAction(Intent.ACTION_GET_CONTENT); + intent.putExtra("return-data", true); + intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + AppLog.v(this, "startActivityForResult"); + startActivityForResult(Intent.createChooser(intent, "Select Picture"), MainActivity.INPUT_FILE_REQUEST_CODE_OLD); + } + }; + + private class JavaScriptInterface { + @SuppressWarnings("unused") + @JavascriptInterface + public void setUserProfile(final String webMessage) throws JSONException { + PodUserProfile pup = ((App)getActivity().getApplication()).getPodUserProfile(); + AppLog.v(this, "StreamFragment.JavaScriptInterface.setUserProfile()"); + if (pup.isRefreshNeeded()) { + AppLog.v(this, "PodUserProfile needs refresh; Try to parse JSON"); + pup.parseJson(webMessage); + } else { + AppLog.v(this, "No PodUserProfile refresh needed"); + } + } + + @SuppressWarnings("unused") + @JavascriptInterface + public void contentHasBeenShared() { + ((MainActivity)getActivity()).setTextToBeShared(null); + } + } + + @Override + public String getFragmentTag() { + return TAG; + } +} diff --git a/app/src/main/java/com/github/dfa/diaspora_android/fragment/HashtagListFragment.java b/app/src/main/java/com/github/dfa/diaspora_android/fragment/HashtagListFragment.java new file mode 100644 index 00000000..104d6951 --- /dev/null +++ b/app/src/main/java/com/github/dfa/diaspora_android/fragment/HashtagListFragment.java @@ -0,0 +1,146 @@ +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ +package com.github.dfa.diaspora_android.fragment; + +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.github.dfa.diaspora_android.App; +import com.github.dfa.diaspora_android.R; +import com.github.dfa.diaspora_android.activity.MainActivity; +import com.github.dfa.diaspora_android.util.AppLog; +import com.github.dfa.diaspora_android.util.DiasporaUrlHelper; + +/** + * Fragment that shows a list of the Hashtags the user follows + * Created by vanitas on 29.09.16. + */ + +public class HashtagListFragment extends CustomFragment { + + public static final String TAG = "com.github.dfa.diaspora_android.HashtagListFragment"; + + protected RecyclerView followedTagsRecyclerView; + protected String[] followedTags; + protected App app; + protected DiasporaUrlHelper urls; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + AppLog.d(this, "onCreateView()"); + return inflater.inflate(R.layout.hashtag_list__fragment, container, false); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + this.followedTagsRecyclerView = (RecyclerView) view.findViewById(R.id.fragment_followed_tags__recycler_view); + this.app = (App) getActivity().getApplication(); + this.urls = new DiasporaUrlHelper(app.getSettings()); + + followedTags = app.getPodUserProfile().getFollowedTags(); + followedTagsRecyclerView.setHasFixedSize(true); + followedTagsRecyclerView.setNestedScrollingEnabled(false); + + RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this.getContext()); + followedTagsRecyclerView.setLayoutManager(layoutManager); + + final FollowedTagsAdapter adapter = new FollowedTagsAdapter(followedTags, onHashtagClickListener); + followedTagsRecyclerView.setAdapter(adapter); + + //Set window title + getActivity().setTitle(R.string.nav_followed_tags); + } + + @Override + public String getFragmentTag() { + return TAG; + } + + @Override + public void onCreateBottomOptionsMenu(Menu menu, MenuInflater inflater) { + /* Nothing to do */ + } + + @Override + public boolean onBackPressed() { + return false; + } + + protected View.OnClickListener onHashtagClickListener = new View.OnClickListener() { + @Override + public void onClick(View view) { + int itemPosition = followedTagsRecyclerView.getChildLayoutPosition(view); + if(itemPosition > -1 && itemPosition < followedTags.length) { + String tag = followedTags[itemPosition]; + ((MainActivity)getActivity()).openDiasporaUrl(urls.getSearchTagsUrl(tag)); + } + } + }; + + public static class FollowedTagsAdapter extends RecyclerView.Adapter { + private String[] followedTagsList; + private View.OnClickListener itemClickListener; + + public static class ViewHolder extends RecyclerView.ViewHolder { + // each data item is just a string in this case + public TextView title; + + public ViewHolder(View v) { + super(v); + title = (TextView) v.findViewById(R.id.recycler_view__list_item__text); + } + } + + // Provide a suitable constructor (depends on the kind of dataset) + public FollowedTagsAdapter(String[] tags, View.OnClickListener itemClickListener) { + this.followedTagsList = tags; + this.itemClickListener = itemClickListener; + } + + @Override + public FollowedTagsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.recycler_view__list_item, parent, false); + v.setOnClickListener(itemClickListener); + return new ViewHolder(v); + } + + // Replace the contents of a view (invoked by the layout manager) + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + + holder.title.setText(followedTagsList[position]); + + } + + // Return the size of your dataset (invoked by the layout manager) + @Override + public int getItemCount() { + return followedTagsList.length; + } + } +} diff --git a/app/src/main/java/com/github/dfa/diaspora_android/activity/PodSelectionActivity.java b/app/src/main/java/com/github/dfa/diaspora_android/fragment/PodSelectionFragment.java similarity index 61% rename from app/src/main/java/com/github/dfa/diaspora_android/activity/PodSelectionActivity.java rename to app/src/main/java/com/github/dfa/diaspora_android/fragment/PodSelectionFragment.java index 9729b8e4..9a2ad317 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/activity/PodSelectionActivity.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/fragment/PodSelectionFragment.java @@ -16,7 +16,7 @@ If not, see . */ -package com.github.dfa.diaspora_android.activity; +package com.github.dfa.diaspora_android.fragment; import android.app.AlertDialog; import android.content.BroadcastReceiver; @@ -28,64 +28,103 @@ import android.os.Build; import android.os.Bundle; import android.support.design.widget.Snackbar; import android.support.v4.content.LocalBroadcastManager; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; import android.text.Editable; import android.text.SpannableString; import android.text.TextWatcher; import android.text.util.Linkify; +import android.view.LayoutInflater; import android.view.Menu; +import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.webkit.CookieManager; +import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.EditText; +import android.widget.ImageView; import android.widget.ListView; import com.github.dfa.diaspora_android.App; import com.github.dfa.diaspora_android.R; +import com.github.dfa.diaspora_android.activity.MainActivity; +import com.github.dfa.diaspora_android.data.AppSettings; import com.github.dfa.diaspora_android.task.GetPodsService; -import com.github.dfa.diaspora_android.util.Helpers; +import com.github.dfa.diaspora_android.util.AppLog; +import com.github.dfa.diaspora_android.util.DiasporaUrlHelper; import com.github.dfa.diaspora_android.util.WebHelper; import java.util.ArrayList; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.OnItemClick; +/** + * Fragment that lets the user choose a Pod + * Created by vanitas on 01.10.16. + */ +public class PodSelectionFragment extends CustomFragment { + public static final String TAG = "com.github.dfa.diaspora_android.PodSelectionFragment"; -public class PodSelectionActivity extends AppCompatActivity { - private App app; + protected EditText editFilter; + protected ListView listPods; + protected ImageView selectPodButton; - @BindView(R.id.podselection__edit_filter) - EditText editFilter; - - @BindView(R.id.podselection__listpods) - ListView listPods; - - @BindView(R.id.toolbar) - Toolbar toolbar; + protected App app; + protected AppSettings appSettings; @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.podselection__activity); - ButterKnife.bind(this); - app = (App) getApplication(); - setSupportActionBar(toolbar); - - - listPods.setTextFilterEnabled(true); - setListedPods(app.getSettings().getPreviousPodlist()); - LocalBroadcastManager.getInstance(this).registerReceiver(podListReceiver, new IntentFilter(GetPodsService.MESSAGE_PODS_RECEIVED)); - - if (!WebHelper.isOnline(PodSelectionActivity.this)) { - Snackbar.make(listPods, R.string.no_internet, Snackbar.LENGTH_LONG).show(); - } + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + AppLog.d(this, "onCreateView()"); + return inflater.inflate(R.layout.podselection__fragment, container, false); } + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + this.app = (App) getActivity().getApplication(); + this.appSettings = app.getSettings(); + + this.editFilter = (EditText) view.findViewById(R.id.podselection__edit_filter); + this.listPods = (ListView) view.findViewById(R.id.podselection__listpods); + this.selectPodButton = (ImageView) view.findViewById(R.id.podselection__button_select_pod); + + listPods.setTextFilterEnabled(true); + listPods.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, int i, long l) { + showPodConfirmationDialog((String) listPods.getAdapter().getItem(i)); + } + }); + setListedPods(appSettings.getPreviousPodlist()); + LocalBroadcastManager.getInstance(getContext()).registerReceiver(podListReceiver, new IntentFilter(GetPodsService.MESSAGE_PODS_RECEIVED)); + if (!WebHelper.isOnline(getContext())) { + Snackbar.make(listPods, R.string.no_internet, Snackbar.LENGTH_LONG).show(); + } + selectPodButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (editFilter.getText().length() > 4 && editFilter.getText().toString().contains("")) { + showPodConfirmationDialog(editFilter.getText().toString()); + } else { + Snackbar.make(listPods, R.string.valid_pod, Snackbar.LENGTH_LONG).show(); + } + } + }); + } + + @Override + public String getFragmentTag() { + return TAG; + } + + @Override + public void onCreateBottomOptionsMenu(Menu menu, MenuInflater inflater) { + /* Nothing to do */ + } + + @Override + public boolean onBackPressed() { + return false; + } private final BroadcastReceiver podListReceiver = new BroadcastReceiver() { @Override @@ -104,20 +143,11 @@ public class PodSelectionActivity extends AppCompatActivity { } }; - @OnClick(R.id.podselection__button_select_pod) - public void onButtonSelectPodClicked(View view) { - if (editFilter.getText().length() > 4 && editFilter.getText().toString().contains("")) { - showPodConfirmationDialog(editFilter.getText().toString()); - } else { - Snackbar.make(listPods, R.string.valid_pod, Snackbar.LENGTH_LONG).show(); - } - } - @Override - protected void onResume() { + public void onResume() { super.onResume(); - Intent i = new Intent(PodSelectionActivity.this, GetPodsService.class); - startService(i); + Intent i = new Intent(getContext(), GetPodsService.class); + getContext().startService(i); } @@ -128,7 +158,7 @@ public class PodSelectionActivity extends AppCompatActivity { } final ArrayAdapter adapter = new ArrayAdapter<>( - PodSelectionActivity.this, + getContext(), android.R.layout.simple_list_item_1, listedPodsList); @@ -154,24 +184,19 @@ public class PodSelectionActivity extends AppCompatActivity { }); } - @OnItemClick(R.id.podselection__listpods) - public void onListPodsItemClicked(int position) { - showPodConfirmationDialog((String) listPods.getAdapter().getItem(position)); - } - private void showPodConfirmationDialog(final String selectedPod) { // Make a clickable link final SpannableString dialogMessage = new SpannableString(getString(R.string.confirm_pod, selectedPod)); Linkify.addLinks(dialogMessage, Linkify.ALL); // Check if online - if (!WebHelper.isOnline(PodSelectionActivity.this)) { + if (!WebHelper.isOnline(getContext())) { Snackbar.make(listPods, R.string.no_internet, Snackbar.LENGTH_LONG).show(); return; } // Show dialog - new AlertDialog.Builder(PodSelectionActivity.this) + new AlertDialog.Builder(getContext()) .setTitle(getString(R.string.confirmation)) .setMessage(dialogMessage) .setPositiveButton(android.R.string.yes, @@ -201,40 +226,28 @@ public class PodSelectionActivity extends AppCompatActivity { e.printStackTrace(); } - Helpers.animateToActivity(this, MainActivity.class, true); - } - - - @Override - public void onBackPressed() { - Snackbar.make(listPods, R.string.confirm_exit, Snackbar.LENGTH_LONG) - .setAction(android.R.string.yes, new View.OnClickListener() { - public void onClick(View view) { - finish(); - } - }) - .show(); + ((MainActivity)getActivity()).openDiasporaUrl(new DiasporaUrlHelper(appSettings).getPodUrl()); } @Override - protected void onDestroy() { - LocalBroadcastManager.getInstance(this).unregisterReceiver(podListReceiver); + public void onDestroy() { + LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(podListReceiver); super.onDestroy(); } @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.podselection__menu, menu); - return true; + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.podselection__menu, menu); + super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_reload: { - if (WebHelper.isOnline(PodSelectionActivity.this)) { - Intent i = new Intent(PodSelectionActivity.this, GetPodsService.class); - startService(i); + if (WebHelper.isOnline(getContext())) { + Intent i = new Intent(getContext(), GetPodsService.class); + getContext().startService(i); return true; } else { Snackbar.make(listPods, R.string.no_internet, Snackbar.LENGTH_LONG).show(); diff --git a/app/src/main/java/com/github/dfa/diaspora_android/receivers/OpenExternalLinkReceiver.java b/app/src/main/java/com/github/dfa/diaspora_android/receiver/OpenExternalLinkReceiver.java similarity index 75% rename from app/src/main/java/com/github/dfa/diaspora_android/receivers/OpenExternalLinkReceiver.java rename to app/src/main/java/com/github/dfa/diaspora_android/receiver/OpenExternalLinkReceiver.java index 0ef48e4f..e52b24ea 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/receivers/OpenExternalLinkReceiver.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/receiver/OpenExternalLinkReceiver.java @@ -1,4 +1,22 @@ -package com.github.dfa.diaspora_android.receivers; +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ +package com.github.dfa.diaspora_android.receiver; import android.app.Activity; import android.content.BroadcastReceiver; @@ -9,7 +27,6 @@ import android.graphics.BitmapFactory; import android.net.Uri; import android.support.customtabs.CustomTabsIntent; -import com.github.dfa.diaspora_android.App; import com.github.dfa.diaspora_android.R; import com.github.dfa.diaspora_android.activity.MainActivity; import com.github.dfa.diaspora_android.data.AppSettings; @@ -17,7 +34,6 @@ import com.github.dfa.diaspora_android.util.AppLog; import com.github.dfa.diaspora_android.util.CustomTabHelpers.BrowserFallback; import com.github.dfa.diaspora_android.util.CustomTabHelpers.CustomTabActivityHelper; import com.github.dfa.diaspora_android.util.Helpers; -import com.github.dfa.diaspora_android.util.Log; /** * BroadcastReceiver that opens links in a Chrome CustomTab diff --git a/app/src/main/java/com/github/dfa/diaspora_android/receivers/UpdateTitleReceiver.java b/app/src/main/java/com/github/dfa/diaspora_android/receiver/UpdateTitleReceiver.java similarity index 80% rename from app/src/main/java/com/github/dfa/diaspora_android/receivers/UpdateTitleReceiver.java rename to app/src/main/java/com/github/dfa/diaspora_android/receiver/UpdateTitleReceiver.java index 10ddc5f5..ca8eda16 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/receivers/UpdateTitleReceiver.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/receiver/UpdateTitleReceiver.java @@ -1,4 +1,22 @@ -package com.github.dfa.diaspora_android.receivers; +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ +package com.github.dfa.diaspora_android.receiver; import android.content.BroadcastReceiver; import android.content.Context; diff --git a/app/src/main/java/com/github/dfa/diaspora_android/ui/BadgeDrawable.java b/app/src/main/java/com/github/dfa/diaspora_android/ui/BadgeDrawable.java index 03460aa9..fab514ab 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/ui/BadgeDrawable.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/ui/BadgeDrawable.java @@ -1,3 +1,21 @@ +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ package com.github.dfa.diaspora_android.ui; import android.content.Context; diff --git a/app/src/main/java/com/github/dfa/diaspora_android/ui/ContextMenuWebView.java b/app/src/main/java/com/github/dfa/diaspora_android/ui/ContextMenuWebView.java index adac6fd2..3be4ce63 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/ui/ContextMenuWebView.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/ui/ContextMenuWebView.java @@ -123,22 +123,49 @@ public class ContextMenuWebView extends NestedWebView { case ID_SHARE_IMAGE: if (url != null) { - final Uri local = Uri.parse(Environment.getExternalStorageDirectory() + "/Pictures/Diaspora/" + System.currentTimeMillis() + ".png"); - new ImageDownloadTask(null, local.getPath()) { - @Override - protected void onPostExecute(Bitmap result) { - Uri myUri = Uri.fromFile(new File(local.getPath())); - Intent sharingIntent = new Intent(); - sharingIntent.setAction(Intent.ACTION_SEND); - sharingIntent.putExtra(Intent.EXTRA_STREAM, myUri); - sharingIntent.setType("image/png"); - sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - context.startActivity(Intent.createChooser(sharingIntent, "Share image using")); + boolean writeToStoragePermitted = true; + if (android.os.Build.VERSION.SDK_INT >= 23) { + int hasWRITE_EXTERNAL_STORAGE = parentActivity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE); + if (hasWRITE_EXTERNAL_STORAGE != PackageManager.PERMISSION_GRANTED) { + writeToStoragePermitted = false; + if (!parentActivity.shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + new AlertDialog.Builder(parentActivity) + .setMessage(R.string.permissions_image) + .setPositiveButton(context.getText(android.R.string.yes), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (android.os.Build.VERSION.SDK_INT >= 23) + parentActivity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + MainActivity.REQUEST_CODE__ACCESS_EXTERNAL_STORAGE); + } + }) + .setNegativeButton(context.getText(android.R.string.no), null) + .show(); + } else { + parentActivity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + MainActivity.REQUEST_CODE__ACCESS_EXTERNAL_STORAGE); + } } - }.execute(url); + } + if (writeToStoragePermitted) { + final Uri local = Uri.parse(Environment.getExternalStorageDirectory() + "/Pictures/Diaspora/" + System.currentTimeMillis() + ".png"); + new ImageDownloadTask(null, local.getPath()) { + @Override + protected void onPostExecute(Bitmap result) { + Uri myUri = Uri.fromFile(new File(local.getPath())); + Intent sharingIntent = new Intent(); + sharingIntent.setAction(Intent.ACTION_SEND); + sharingIntent.putExtra(Intent.EXTRA_STREAM, myUri); + sharingIntent.setType("image/png"); + sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + context.startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.action_share_dotdotdot))); + } + }.execute(url); + } } else { Toast.makeText(context, "Cannot share image: url is null", Toast.LENGTH_SHORT).show(); } + break; case ID_IMAGE_EXTERNAL_BROWSER: diff --git a/app/src/main/java/com/github/dfa/diaspora_android/util/AppLog.java b/app/src/main/java/com/github/dfa/diaspora_android/util/AppLog.java index 171bbd6c..178cd008 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/util/AppLog.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/util/AppLog.java @@ -1,3 +1,21 @@ +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ package com.github.dfa.diaspora_android.util; /** diff --git a/app/src/main/java/com/github/dfa/diaspora_android/util/CustomTabHelpers/BrowserFallback.java b/app/src/main/java/com/github/dfa/diaspora_android/util/CustomTabHelpers/BrowserFallback.java index a4bdd930..4e70f1c0 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/util/CustomTabHelpers/BrowserFallback.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/util/CustomTabHelpers/BrowserFallback.java @@ -1,3 +1,21 @@ +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ package com.github.dfa.diaspora_android.util.CustomTabHelpers; import android.app.Activity; diff --git a/app/src/main/java/com/github/dfa/diaspora_android/util/CustomTabHelpers/CustomTabActivityHelper.java b/app/src/main/java/com/github/dfa/diaspora_android/util/CustomTabHelpers/CustomTabActivityHelper.java index e43ce0b0..b515adcc 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/util/CustomTabHelpers/CustomTabActivityHelper.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/util/CustomTabHelpers/CustomTabActivityHelper.java @@ -1,3 +1,21 @@ +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ package com.github.dfa.diaspora_android.util.CustomTabHelpers; import android.app.Activity; @@ -118,6 +136,10 @@ public class CustomTabActivityHelper { return session.mayLaunchUrl(uri, extras, otherLikelyBundles); } + public boolean warmup(int flags) { + return mClient.warmup(flags); + } + /** * A Callback for when the service is connected or disconnected. Use those callbacks to * handle UI changes when the service is connected or disconnected diff --git a/app/src/main/java/com/github/dfa/diaspora_android/util/CustomTabHelpers/CustomTabsHelper.java b/app/src/main/java/com/github/dfa/diaspora_android/util/CustomTabHelpers/CustomTabsHelper.java index 6ab577af..c1b9c546 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/util/CustomTabHelpers/CustomTabsHelper.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/util/CustomTabHelpers/CustomTabsHelper.java @@ -1,3 +1,21 @@ +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ package com.github.dfa.diaspora_android.util.CustomTabHelpers; import android.content.Context; diff --git a/app/src/main/java/com/github/dfa/diaspora_android/util/Log.java b/app/src/main/java/com/github/dfa/diaspora_android/util/Log.java index ad0791e7..f8743b1d 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/util/Log.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/util/Log.java @@ -1,3 +1,21 @@ +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ package com.github.dfa.diaspora_android.util; import com.github.dfa.diaspora_android.data.AppSettings; diff --git a/app/src/main/java/com/github/dfa/diaspora_android/util/WebHelper.java b/app/src/main/java/com/github/dfa/diaspora_android/util/WebHelper.java index 691481be..ba760d7b 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/util/WebHelper.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/util/WebHelper.java @@ -62,7 +62,7 @@ public class WebHelper { public static void optimizeMobileSiteLayout(final WebView wv) { wv.loadUrl("javascript: ( function() {" + " if (document.documentElement == null || document.documentElement.style == null) { return; }" + - " document.documentElement.style.paddingBottom = '260px';" + + " document.documentElement.style.paddingBottom = '50px';" + " document.getElementById('main').style.paddingTop = '5px';" + " if(document.getElementById('main_nav')) {" + " document.getElementById('main_nav').parentNode.removeChild(" + diff --git a/app/src/main/java/com/github/dfa/diaspora_android/ui/CustomWebViewClient.java b/app/src/main/java/com/github/dfa/diaspora_android/webview/CustomWebViewClient.java similarity index 95% rename from app/src/main/java/com/github/dfa/diaspora_android/ui/CustomWebViewClient.java rename to app/src/main/java/com/github/dfa/diaspora_android/webview/CustomWebViewClient.java index 8e31a15c..914abcbb 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/ui/CustomWebViewClient.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/webview/CustomWebViewClient.java @@ -16,7 +16,7 @@ If not, see . */ -package com.github.dfa.diaspora_android.ui; +package com.github.dfa.diaspora_android.webview; import android.content.Intent; import android.net.Uri; @@ -37,6 +37,7 @@ public class CustomWebViewClient extends WebViewClient { this.webView = webView; } + //Open non-diaspora links in customtab/external browser public boolean shouldOverrideUrlLoading(WebView view, String url) { if (!url.contains(app.getSettings().getPodDomain())) { Intent i = new Intent(MainActivity.ACTION_OPEN_EXTERNAL_URL); diff --git a/app/src/main/java/com/github/dfa/diaspora_android/webview/DiasporaStreamWebChromeClient.java b/app/src/main/java/com/github/dfa/diaspora_android/webview/DiasporaStreamWebChromeClient.java new file mode 100644 index 00000000..f3572e97 --- /dev/null +++ b/app/src/main/java/com/github/dfa/diaspora_android/webview/DiasporaStreamWebChromeClient.java @@ -0,0 +1,62 @@ +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ +package com.github.dfa.diaspora_android.webview; + +import android.webkit.WebView; +import android.widget.ProgressBar; + +import com.github.dfa.diaspora_android.util.AppLog; +import com.github.dfa.diaspora_android.util.WebHelper; + +/** + * Created by vanitas on 26.09.16. + */ + +public class DiasporaStreamWebChromeClient extends FileUploadWebChromeClient { + protected SharedTextCallback sharedTextCallback; + + public DiasporaStreamWebChromeClient(WebView webView, ProgressBar progressBar, FileUploadCallback fileUploadCallback, SharedTextCallback callback) { + super(webView, progressBar, fileUploadCallback); + this.sharedTextCallback = callback; + } + + @Override + public void onProgressChanged(WebView wv, int progress) { + super.onProgressChanged(wv, progress); + if (progress > 0 && progress <= 60) { + WebHelper.getUserProfile(wv); + WebHelper.optimizeMobileSiteLayout(wv); + } + + if (progress > 60) { + WebHelper.optimizeMobileSiteLayout(wv); + + String textToBeShared = sharedTextCallback.getSharedText(); + if (textToBeShared != null) { + AppLog.d(this, "Share text into webView"); + WebHelper.shareTextIntoWebView(wv, textToBeShared); + } + } + } + + public interface SharedTextCallback { + String getSharedText(); + void setSharedText(String shared); + } +} diff --git a/app/src/main/java/com/github/dfa/diaspora_android/webview/FileUploadWebChromeClient.java b/app/src/main/java/com/github/dfa/diaspora_android/webview/FileUploadWebChromeClient.java new file mode 100644 index 00000000..a730182e --- /dev/null +++ b/app/src/main/java/com/github/dfa/diaspora_android/webview/FileUploadWebChromeClient.java @@ -0,0 +1,59 @@ +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ +package com.github.dfa.diaspora_android.webview; + +import android.net.Uri; +import android.webkit.ValueCallback; +import android.webkit.WebView; +import android.widget.ProgressBar; + +/** + * Created by vanitas on 26.09.16. + */ + +public class FileUploadWebChromeClient extends ProgressBarWebChromeClient { + protected FileUploadCallback fileUploadCallback; + + public FileUploadWebChromeClient(WebView webView, ProgressBar progressBar, FileUploadCallback fileUploadCallback) { + super(webView, progressBar); + this.fileUploadCallback = fileUploadCallback; + } + + @Override + public void onProgressChanged(WebView wv, int progress) { + super.onProgressChanged(wv, progress); + } + + //For Android 4.1/4.2 only. DO NOT REMOVE! + @SuppressWarnings("unused") + protected void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) + { + fileUploadCallback.legacyImageUpload(uploadMsg, acceptType, capture); + } + + @Override + public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { + return fileUploadCallback.imageUpload(webView, filePathCallback, fileChooserParams); + } + + public interface FileUploadCallback { + boolean imageUpload(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams); + void legacyImageUpload(ValueCallback uploadMsg, String acceptType, String capture); + } +} diff --git a/app/src/main/java/com/github/dfa/diaspora_android/webview/ProgressBarWebChromeClient.java b/app/src/main/java/com/github/dfa/diaspora_android/webview/ProgressBarWebChromeClient.java new file mode 100644 index 00000000..ec5c7b41 --- /dev/null +++ b/app/src/main/java/com/github/dfa/diaspora_android/webview/ProgressBarWebChromeClient.java @@ -0,0 +1,44 @@ +/* + This file is part of the Diaspora for Android. + + Diaspora for Android is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Diaspora for Android is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Diaspora for Android. + + If not, see . + */ +package com.github.dfa.diaspora_android.webview; + +import android.view.View; +import android.webkit.WebChromeClient; +import android.webkit.WebView; +import android.widget.ProgressBar; + +/** + * WebChromeClient that connects the ProgressBar and the WebView and updates the progress of the progressBar. + * Created by vanitas on 26.09.16. + */ + +public class ProgressBarWebChromeClient extends WebChromeClient { + protected final ProgressBar progressBar; + protected final WebView webView; + + public ProgressBarWebChromeClient(WebView webView, ProgressBar progressBar) { + this.webView = webView; + this.progressBar = progressBar; + } + + public void onProgressChanged(WebView wv, int progress) { + progressBar.setProgress(progress); + progressBar.setVisibility(progress == 100 ? View.GONE : View.VISIBLE); + } +} diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 00000000..11f95124 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_launcher.png b/app/src/main/res/drawable-ldpi/ic_launcher.png new file mode 100644 index 00000000..8e89a6a9 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 00000000..b356e40a Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 00000000..5055f6cf Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..2001d858 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..0d77fa66 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable/ic_launcher.xml b/app/src/main/res/drawable/ic_launcher.xml deleted file mode 100644 index 4d66007f..00000000 --- a/app/src/main/res/drawable/ic_launcher.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/app/src/main/res/layout/about__activity.xml b/app/src/main/res/layout/about__activity.xml index c686e81e..6a8c3568 100644 --- a/app/src/main/res/layout/about__activity.xml +++ b/app/src/main/res/layout/about__activity.xml @@ -17,7 +17,7 @@ + android:id="@+id/main__topbar"/> + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/hashtag_list__fragment.xml b/app/src/main/res/layout/hashtag_list__fragment.xml new file mode 100644 index 00000000..94df572b --- /dev/null +++ b/app/src/main/res/layout/hashtag_list__fragment.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/main__activity.xml b/app/src/main/res/layout/main__activity.xml index a546cd6d..7de0204c 100644 --- a/app/src/main/res/layout/main__activity.xml +++ b/app/src/main/res/layout/main__activity.xml @@ -2,7 +2,7 @@ - + - + - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/podselection__activity.xml b/app/src/main/res/layout/podselection__activity.xml deleted file mode 100644 index 418a66b4..00000000 --- a/app/src/main/res/layout/podselection__activity.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/podselection__fragment.xml b/app/src/main/res/layout/podselection__fragment.xml new file mode 100644 index 00000000..b8e39ac4 --- /dev/null +++ b/app/src/main/res/layout/podselection__fragment.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_view__list_item.xml b/app/src/main/res/layout/recycler_view__list_item.xml new file mode 100644 index 00000000..764467e7 --- /dev/null +++ b/app/src/main/res/layout/recycler_view__list_item.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/splash__activity.xml b/app/src/main/res/layout/splash__activity.xml deleted file mode 100644 index 5f65f563..00000000 --- a/app/src/main/res/layout/splash__activity.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/ui__webview.xml b/app/src/main/res/layout/ui__webview.xml deleted file mode 100644 index 35d12bc4..00000000 --- a/app/src/main/res/layout/ui__webview.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/main__menu_bottom.xml b/app/src/main/res/menu/main__menu_bottom.xml index 002a2a29..0c973856 100644 --- a/app/src/main/res/menu/main__menu_bottom.xml +++ b/app/src/main/res/menu/main__menu_bottom.xml @@ -8,42 +8,31 @@ android:id="@+id/action_search" android:icon="@drawable/ic_search_white_48px" app:showAsAction="always" + android:orderInCategory="100" android:title="@string/action_search_by_tags_or_persons" /> - - - - - - - - - + app:showAsAction="always" + android:orderInCategory="400" + android:title="@string/action_go_to_top" /> - + /> diff --git a/app/src/main/res/menu/main__menu_top.xml b/app/src/main/res/menu/main__menu_top.xml index d6c873b7..73ea3bc7 100644 --- a/app/src/main/res/menu/main__menu_top.xml +++ b/app/src/main/res/menu/main__menu_top.xml @@ -6,19 +6,16 @@ + /> - - + app:showAsAction="always" + android:orderInCategory="200" + android:title="@string/conversations" /> diff --git a/app/src/main/res/menu/main__navdrawer.xml b/app/src/main/res/menu/main__navdrawer.xml index 25a2f7a9..56323eb8 100644 --- a/app/src/main/res/menu/main__navdrawer.xml +++ b/app/src/main/res/menu/main__navdrawer.xml @@ -61,7 +61,7 @@ android:title="@string/settings" /> diff --git a/app/src/main/res/menu/podselection__menu.xml b/app/src/main/res/menu/podselection__menu.xml index 1c33e1c9..8b498aa3 100644 --- a/app/src/main/res/menu/podselection__menu.xml +++ b/app/src/main/res/menu/podselection__menu.xml @@ -1,7 +1,5 @@ + xmlns:app="http://schemas.android.com/apk/res-auto"> + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/stream__menu_top.xml b/app/src/main/res/menu/stream__menu_top.xml new file mode 100644 index 00000000..9d948098 --- /dev/null +++ b/app/src/main/res/menu/stream__menu_top.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-de/strings-preferences.xml b/app/src/main/res/values-de/strings-preferences.xml index c629a28e..22ba823a 100644 --- a/app/src/main/res/values-de/strings-preferences.xml +++ b/app/src/main/res/values-de/strings-preferences.xml @@ -21,13 +21,14 @@ Lade Bilder Deaktiviere das Laden von Bildern, um den Datenverbrauch zu verringern + Proxy Aktiviere Netzwerkproxy Nutze einen Proxyserver, um Firewalls zu umgehen Host Port Chrome Custom Tabs - Externe Links mit Chrome Custom Tabs öffnen. Für dieses Feature muss Chromium oder Google Chrome installiert sein + Externe Links mit Chrome Custom Tabs öffnen. Chromium oder Google Chrome muss für dieses Feature installiert sein.\nWICHTIGER HINWEIS: Chrome Custom Tabs verwenden die konfigurierten Proxy-Server nicht! Persönliche Einstellungen Öffne die Einstellungen deines Diaspora Accounts diff --git a/app/src/main/res/values-fr/strings-preferences.xml b/app/src/main/res/values-fr/strings-preferences.xml index 2ff27c03..f48dffe9 100644 --- a/app/src/main/res/values-fr/strings-preferences.xml +++ b/app/src/main/res/values-fr/strings-preferences.xml @@ -21,13 +21,13 @@ Charger les images Désactiver le chargements des images pour préserver la data mobile + Proxy Activer Proxy Serveur Proxy.\n(Nécessite un redémarrage) Hôte Port Onglets personnalisés de Chrome - Ouvrir les liens externes avec les onglets personnalisés. Chromium ou Google Chrome doit être installé pour cette fonctionnalité Paramètres personnels Ouvrir vos paramètres de compte diaspora diff --git a/app/src/main/res/values-it/strings-preferences.xml b/app/src/main/res/values-it/strings-preferences.xml index 7ace8cf5..5ba1e1df 100644 --- a/app/src/main/res/values-it/strings-preferences.xml +++ b/app/src/main/res/values-it/strings-preferences.xml @@ -21,11 +21,13 @@ Carica immagini Disabilita il caricamento delle immagini per risparmiare la rete dati + Proxy Attiva proxy Traffico del proxy di Diaspora per bypassare i firewall.\nPuò essere necessario il riavvio dell\'app Host Porta + Schede personalizzate di Chrome Impostazioni personali Apri le impostazioni del tuo account Diaspora diff --git a/app/src/main/res/values-ja/strings-preferences.xml b/app/src/main/res/values-ja/strings-preferences.xml index de7154e6..fd414294 100644 --- a/app/src/main/res/values-ja/strings-preferences.xml +++ b/app/src/main/res/values-ja/strings-preferences.xml @@ -21,13 +21,13 @@ 画像の読み込み 安全なモバイルデータのため、画像の読み込みを無効にします + プロキシ プロキシを有効にする Diaspora の通信をプロキシして、ファイアウォールに回避します。\n再起動が必要になることがあります ホスト ポート Chrome カスタムタブ - Chrome カスタム タブで外部リンクを開きます。この機能は Chromium または Google Chrome をインストールする必要があります 個人用設定 Diaspora アカウント設定を開きます diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 1009fc6a..35131109 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -7,6 +7,7 @@ 16dp 16dp 8dp + 45dp diff --git a/app/src/main/res/values/strings-preferences.xml b/app/src/main/res/values/strings-preferences.xml index 225eb33b..0d31c0ca 100644 --- a/app/src/main/res/values/strings-preferences.xml +++ b/app/src/main/res/values/strings-preferences.xml @@ -85,7 +85,7 @@ Load Tor Preset Load proxy settings for Tor (Orbot) HTTP Proxy Proxy - @string/pref_desc__http_proxy_enabled + @string/pref_desc__http_proxy_enabled Enable Proxy Proxy Diaspora\'s traffic to circumvent firewalls.\nMay require restart Host @@ -96,7 +96,7 @@ Chrome Custom Tabs - Open external links with Chrome Custom Tabs. Chromium or Google Chrome needs to be installed for this feature + Open external links with Chrome Custom Tabs. Chromium or Google Chrome needs to be installed for this feature. \nIMPORTANT NOTE: Chrome Custom Tabs do not use configured proxy servers! Personal settings diff --git a/build.gradle b/build.gradle index 052b2e6a..54903cde 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.3' + classpath 'com.android.tools.build:gradle:2.2.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' // NOTE: Do not place your application dependencies here; they belong diff --git a/tools/localization/.gitignore b/tools/localization/.gitignore deleted file mode 100755 index 98626981..00000000 --- a/tools/localization/.gitignore +++ /dev/null @@ -1 +0,0 @@ -crowdin.yaml diff --git a/tools/localization/downloadLocalizations.sh b/tools/localization/downloadLocalizations.sh deleted file mode 100755 index 86fc3252..00000000 --- a/tools/localization/downloadLocalizations.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -######################################################### -# -# Title -# -# Created by Gregor Santer (gsantner), 2016 -# https://gsantner.github.io/ -# -######################################################### - - -#Pfade -SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -SCRIPTFILE=$(readlink -f $0) -SCRIPTPATH=$(dirname $SCRIPTFILE) -argc=$# - -######################################################### -cd "$SCRIPTDIR" - -if [ ! -f "crowdin.yaml" ] ; then - echo "project_identifier: diaspora-for-android" > 'crowdin.yaml' - echo "base_path: $(realpath '../../')" >>'crowdin.yaml' - echo "api_key: DONT_PUSH_API_KEY" >>'crowdin.yaml' - cat "../../crowdin.yaml" >> "crowdin.yaml" - echo "# Add all non locality languages here" >> "crowdin.yaml" - echo "# (e.g. enUS, enUK, deCH, deAT will automatically go into the right folder)" >> "crowdin.yaml" - echo "# Otherwise e.g. en would get added into the folder enEN (which is wrong)." >> "crowdin.yaml" - echo "# https://crowdin.com/page/api/language-codes contains supported language codes" >> "crowdin.yaml" - echo "# The first listed ones here are diffently managed by crowdin than on android" >> "crowdin.yaml" -fi - -if grep -q "DONT_PUSH" "crowdin.yaml" ; then - echo "Insert API key to crowdin.yaml" - echo "and update folder to the root folder of the repository" - exit -fi - -# Load latest translations -crowdin-cli download -b master