WhatsNewActivity.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /*
  2. * Nextcloud Android client application
  3. *
  4. * @author Bartosz Przybylski
  5. * Copyright (C) 2015 Bartosz Przybylski
  6. * Copyright (C) 2015 ownCloud Inc.
  7. * Copyright (C) 2016 Nextcloud.
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  11. * License as published by the Free Software Foundation; either
  12. * version 3 of the License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public
  20. * License along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. package com.owncloud.android.ui.activity;
  23. import android.accounts.Account;
  24. import android.accounts.AccountManager;
  25. import android.content.Context;
  26. import android.content.Intent;
  27. import android.content.SharedPreferences;
  28. import android.os.Build;
  29. import android.os.Bundle;
  30. import android.preference.PreferenceManager;
  31. import android.support.annotation.Nullable;
  32. import android.support.v4.app.Fragment;
  33. import android.support.v4.app.FragmentActivity;
  34. import android.support.v4.app.FragmentManager;
  35. import android.support.v4.app.FragmentPagerAdapter;
  36. import android.support.v4.view.ViewPager;
  37. import android.text.SpannableString;
  38. import android.text.Spanned;
  39. import android.text.style.BulletSpan;
  40. import android.view.Gravity;
  41. import android.view.LayoutInflater;
  42. import android.view.View;
  43. import android.view.ViewGroup;
  44. import android.webkit.WebView;
  45. import android.webkit.WebViewClient;
  46. import android.widget.Button;
  47. import android.widget.ImageButton;
  48. import android.widget.ImageView;
  49. import android.widget.LinearLayout;
  50. import android.widget.TextView;
  51. import com.owncloud.android.MainApp;
  52. import com.owncloud.android.R;
  53. import com.owncloud.android.authentication.AccountAuthenticatorActivity;
  54. import com.owncloud.android.authentication.AccountUtils;
  55. import com.owncloud.android.features.FeatureList;
  56. import com.owncloud.android.features.FeatureList.FeatureItem;
  57. import com.owncloud.android.ui.whatsnew.ProgressIndicator;
  58. import com.owncloud.android.utils.AnalyticsUtils;
  59. import com.owncloud.android.utils.ThemeUtils;
  60. /**
  61. * Activity displaying general feature after a fresh install and new features after an update.
  62. */
  63. public class WhatsNewActivity extends FragmentActivity implements ViewPager.OnPageChangeListener {
  64. public static final String KEY_LAST_SEEN_VERSION_CODE = "lastSeenVersionCode";
  65. private static final String SCREEN_NAME = "What's new";
  66. private static final String TAG = WhatsNewActivity.class.getSimpleName();
  67. private ImageButton mForwardFinishButton;
  68. private Button mSkipButton;
  69. private ProgressIndicator mProgress;
  70. private ViewPager mPager;
  71. @Override
  72. protected void onCreate(Bundle savedInstanceState) {
  73. super.onCreate(savedInstanceState);
  74. setContentView(R.layout.whats_new_activity);
  75. int fontColor = ThemeUtils.fontColor();
  76. mProgress = findViewById(R.id.progressIndicator);
  77. mPager = findViewById(R.id.contentPanel);
  78. final boolean isBeta = getResources().getBoolean(R.bool.is_beta);
  79. String[] urls = getResources().getStringArray(R.array.whatsnew_urls);
  80. // Sometimes, accounts are not deleted when you uninstall the application so we'll do it now
  81. if (isFirstRun()) {
  82. AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE);
  83. for (Account account : AccountUtils.getAccounts(this)) {
  84. am.removeAccount(account, null, null);
  85. }
  86. }
  87. boolean showWebView = urls.length > 0;
  88. if (showWebView) {
  89. FeaturesWebViewAdapter featuresWebViewAdapter = new FeaturesWebViewAdapter(getSupportFragmentManager(),
  90. urls);
  91. mProgress.setNumberOfSteps(featuresWebViewAdapter.getCount());
  92. mPager.setAdapter(featuresWebViewAdapter);
  93. } else {
  94. FeaturesViewAdapter featuresViewAdapter = new FeaturesViewAdapter(getSupportFragmentManager(),
  95. FeatureList.getFiltered(getLastSeenVersionCode(), isFirstRun(), isBeta));
  96. mProgress.setNumberOfSteps(featuresViewAdapter.getCount());
  97. mPager.setAdapter(featuresViewAdapter);
  98. }
  99. mPager.addOnPageChangeListener(this);
  100. mForwardFinishButton = findViewById(R.id.forward);
  101. ThemeUtils.colorImageButton(mForwardFinishButton, fontColor);
  102. mForwardFinishButton.setOnClickListener(new View.OnClickListener() {
  103. @Override
  104. public void onClick(View view) {
  105. if (mProgress.hasNextStep()) {
  106. mPager.setCurrentItem(mPager.getCurrentItem() + 1, true);
  107. mProgress.animateToStep(mPager.getCurrentItem() + 1);
  108. } else {
  109. onFinish();
  110. finish();
  111. }
  112. updateNextButtonIfNeeded();
  113. }
  114. });
  115. if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
  116. mForwardFinishButton.setBackground(null);
  117. } else {
  118. mForwardFinishButton.setBackgroundDrawable(null);
  119. }
  120. mSkipButton = findViewById(R.id.skip);
  121. mSkipButton.setTextColor(fontColor);
  122. mSkipButton.setOnClickListener(new View.OnClickListener() {
  123. @Override
  124. public void onClick(View view) {
  125. onFinish();
  126. finish();
  127. }
  128. });
  129. TextView tv = findViewById(R.id.welcomeText);
  130. if (showWebView) {
  131. tv.setText(R.string.app_name);
  132. } else if (isFirstRun()) {
  133. tv.setText(R.string.empty);
  134. } else {
  135. tv.setText(String.format(getString(R.string.whats_new_title), MainApp.getVersionName()));
  136. }
  137. updateNextButtonIfNeeded();
  138. }
  139. @Override
  140. protected void onResume() {
  141. super.onResume();
  142. AnalyticsUtils.setCurrentScreenName(this, SCREEN_NAME, TAG);
  143. }
  144. @Override
  145. public void onBackPressed() {
  146. onFinish();
  147. super.onBackPressed();
  148. }
  149. private void updateNextButtonIfNeeded() {
  150. if (!mProgress.hasNextStep()) {
  151. mForwardFinishButton.setImageResource(R.drawable.ic_done_white);
  152. mSkipButton.setVisibility(View.INVISIBLE);
  153. } else {
  154. mForwardFinishButton.setImageResource(R.drawable.arrow_right);
  155. mSkipButton.setVisibility(View.VISIBLE);
  156. }
  157. }
  158. private void onFinish() {
  159. SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
  160. SharedPreferences.Editor editor = pref.edit();
  161. editor.putInt(KEY_LAST_SEEN_VERSION_CODE, MainApp.getVersionCode());
  162. editor.apply();
  163. }
  164. static private int getLastSeenVersionCode() {
  165. SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(MainApp.getAppContext());
  166. return pref.getInt(KEY_LAST_SEEN_VERSION_CODE, 0);
  167. }
  168. static private boolean isFirstRun() {
  169. return getLastSeenVersionCode() == 0 && AccountUtils.getCurrentOwnCloudAccount(MainApp.getAppContext()) == null;
  170. }
  171. static public void runIfNeeded(Context context) {
  172. if (!context.getResources().getBoolean(R.bool.show_whats_new)) {
  173. return;
  174. }
  175. if (context instanceof WhatsNewActivity) {
  176. return;
  177. }
  178. if (shouldShow(context)) {
  179. context.startActivity(new Intent(context, WhatsNewActivity.class));
  180. }
  181. }
  182. static private boolean shouldShow(Context context) {
  183. final boolean isBeta = context.getResources().getBoolean(R.bool.is_beta);
  184. return (isFirstRun() && context instanceof AccountAuthenticatorActivity) ||
  185. (
  186. !(isFirstRun() && (context instanceof FileDisplayActivity)) &&
  187. !(context instanceof PassCodeActivity) &&
  188. (FeatureList.getFiltered(getLastSeenVersionCode(), isFirstRun(), isBeta).length > 0)
  189. );
  190. }
  191. @Override
  192. public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
  193. // unused but to be implemented due to abstract parent
  194. }
  195. @Override
  196. public void onPageSelected(int position) {
  197. mProgress.animateToStep(position + 1);
  198. updateNextButtonIfNeeded();
  199. }
  200. @Override
  201. public void onPageScrollStateChanged(int state) {
  202. // unused but to be implemented due to abstract parent
  203. }
  204. private final class FeaturesWebViewAdapter extends FragmentPagerAdapter {
  205. private String[] mWebUrls;
  206. public FeaturesWebViewAdapter(FragmentManager fm, String[] webUrls) {
  207. super(fm);
  208. mWebUrls = webUrls;
  209. }
  210. @Override
  211. public Fragment getItem(int position) {
  212. return FeatureWebFragment.newInstance(mWebUrls[position]);
  213. }
  214. @Override
  215. public int getCount() {
  216. return mWebUrls.length;
  217. }
  218. }
  219. public static class FeatureWebFragment extends Fragment {
  220. private String mWebUrl;
  221. static public FeatureWebFragment newInstance(String webUrl) {
  222. FeatureWebFragment f = new FeatureWebFragment();
  223. Bundle args = new Bundle();
  224. args.putString("url", webUrl);
  225. f.setArguments(args);
  226. return f;
  227. }
  228. @Override
  229. public void onCreate(@Nullable Bundle savedInstanceState) {
  230. super.onCreate(savedInstanceState);
  231. mWebUrl = getArguments() != null ? getArguments().getString("url") : null;
  232. }
  233. @Nullable
  234. @Override
  235. public View onCreateView(LayoutInflater inflater,
  236. @Nullable ViewGroup container,
  237. @Nullable Bundle savedInstanceState) {
  238. View v = inflater.inflate(R.layout.whats_new_webview_element, container, false);
  239. WebView webView = v.findViewById(R.id.whatsNewWebView);
  240. webView.getSettings().setJavaScriptEnabled(true);
  241. webView.getSettings().setDomStorageEnabled(true);
  242. webView.getSettings().setAllowFileAccess(false);
  243. webView.setWebViewClient(new WebViewClient());
  244. webView.loadUrl(mWebUrl);
  245. return v;
  246. }
  247. }
  248. private final class FeaturesViewAdapter extends FragmentPagerAdapter {
  249. private FeatureItem[] mFeatures;
  250. public FeaturesViewAdapter(FragmentManager fm, FeatureItem[] features) {
  251. super(fm);
  252. mFeatures = features;
  253. }
  254. @Override
  255. public Fragment getItem(int position) {
  256. return FeatureFragment.newInstance(mFeatures[position]);
  257. }
  258. @Override
  259. public int getCount() {
  260. return mFeatures.length;
  261. }
  262. }
  263. public static class FeatureFragment extends Fragment {
  264. private FeatureItem mItem;
  265. static public FeatureFragment newInstance(FeatureItem item) {
  266. FeatureFragment f = new FeatureFragment();
  267. Bundle args = new Bundle();
  268. args.putParcelable("feature", item);
  269. f.setArguments(args);
  270. return f;
  271. }
  272. @Override
  273. public void onCreate(@Nullable Bundle savedInstanceState) {
  274. super.onCreate(savedInstanceState);
  275. mItem = getArguments() != null ? (FeatureItem) getArguments().getParcelable("feature") : null;
  276. }
  277. @Nullable
  278. @Override
  279. public View onCreateView(LayoutInflater inflater,
  280. @Nullable ViewGroup container,
  281. @Nullable Bundle savedInstanceState) {
  282. View v = inflater.inflate(R.layout.whats_new_element, container, false);
  283. int fontColor = ThemeUtils.fontColor();
  284. ImageView iv = v.findViewById(R.id.whatsNewImage);
  285. if (mItem.shouldShowImage()) {
  286. iv.setImageResource(mItem.getImage());
  287. }
  288. TextView titleTextView = v.findViewById(R.id.whatsNewTitle);
  289. if (mItem.shouldShowTitleText()) {
  290. titleTextView.setText(mItem.getTitleText());
  291. titleTextView.setTextColor(fontColor);
  292. }
  293. if (mItem.shouldShowContentText()) {
  294. LinearLayout linearLayout = v.findViewById(R.id.whatsNewTextLayout);
  295. if (mItem.shouldShowBulletPointList()) {
  296. String[] texts = getText(mItem.getContentText()).toString().split("\n");
  297. for (String text : texts) {
  298. TextView textView = generateTextView(text, getContext(),
  299. mItem.shouldContentCentered(), fontColor, true);
  300. linearLayout.addView(textView);
  301. }
  302. } else {
  303. TextView textView = generateTextView(getText(mItem.getContentText()).toString(),
  304. getContext(), mItem.shouldContentCentered(), fontColor, false);
  305. linearLayout.addView(textView);
  306. }
  307. }
  308. return v;
  309. }
  310. }
  311. private static TextView generateTextView(String text, Context context,
  312. boolean shouldContentCentered, int fontColor,
  313. boolean showBulletPoints) {
  314. int standardMargin = context.getResources().getDimensionPixelSize(R.dimen.standard_margin);
  315. int doubleMargin = context.getResources()
  316. .getDimensionPixelSize(R.dimen.standard_double_margin);
  317. int zeroMargin = context.getResources().getDimensionPixelSize(R.dimen.zero);
  318. TextView textView = new TextView(context);
  319. LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
  320. ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
  321. layoutParams.setMargins(doubleMargin, standardMargin, doubleMargin, zeroMargin);
  322. textView.setTextAppearance(context, R.style.NextcloudTextAppearanceMedium);
  323. textView.setLayoutParams(layoutParams);
  324. if (showBulletPoints) {
  325. BulletSpan bulletSpan = new BulletSpan(standardMargin, fontColor);
  326. SpannableString spannableString = new SpannableString(text);
  327. spannableString.setSpan(bulletSpan, 0, spannableString.length(),
  328. Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
  329. textView.setText(spannableString);
  330. } else {
  331. textView.setText(text);
  332. }
  333. textView.setTextColor(fontColor);
  334. if (!shouldContentCentered) {
  335. textView.setGravity(Gravity.START);
  336. } else {
  337. textView.setGravity(Gravity.CENTER_HORIZONTAL);
  338. }
  339. return textView;
  340. }
  341. }