From 877f790495c707fd8fa7c7a72e4911ddfeba8f42 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Wed, 23 Mar 2016 15:47:06 +0100 Subject: [PATCH 01/84] initial rewrite to make use of the toolbar --- AndroidManifest.xml | 8 +- res/layout/drawer.xml | 61 +---- res/layout/drawer_header.xml | 50 ++++ res/layout/files.xml | 46 ++-- res/layout/files_folder_picker.xml | 18 +- res/layout/toolbar_standard.xml | 48 ++++ res/layout/upload_files_layout.xml | 9 +- res/menu/drawer_menu.xml | 38 +++ res/values-v21/styles.xml | 17 +- res/values/styles.xml | 4 +- .../android/ui/activity/DrawerActivity.java | 231 ++++++++++++++++++ .../android/ui/activity/FileActivity.java | 230 +---------------- .../ui/activity/FileDisplayActivity.java | 33 ++- .../ui/activity/FolderPickerActivity.java | 19 +- .../android/ui/activity/ToolbarActivity.java | 92 +++++++ .../ui/activity/UploadFilesActivity.java | 5 +- .../ui/activity/UploadListActivity.java | 10 +- .../ui/preview/PreviewImageActivity.java | 2 +- 18 files changed, 572 insertions(+), 349 deletions(-) create mode 100644 res/layout/drawer_header.xml create mode 100644 res/layout/toolbar_standard.xml create mode 100644 res/menu/drawer_menu.xml create mode 100644 src/com/owncloud/android/ui/activity/DrawerActivity.java create mode 100644 src/com/owncloud/android/ui/activity/ToolbarActivity.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 584de1126b8..be718438c94 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -59,11 +59,13 @@ android:name=".MainApp" android:icon="@mipmap/icon" android:label="@string/app_name" - android:theme="@style/Theme.ownCloud" + android:theme="@style/Theme.ownCloud.Toolbar" android:manageSpaceActivity=".ui.activity.ManageSpaceActivity" android:resizeableActivity="true" android:supportsPictureInPicture="false" > - + @@ -165,7 +167,7 @@ android:name=".authentication.AuthenticatorActivity" android:exported="true" android:launchMode="singleTask" - android:theme="@style/Theme.ownCloud.noActionBar" > + android:theme="@style/Theme.ownCloud.Toolbar" > diff --git a/res/layout/drawer.xml b/res/layout/drawer.xml index 97ea8127a74..4bccbd79533 100644 --- a/res/layout/drawer.xml +++ b/res/layout/drawer.xml @@ -16,18 +16,18 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - - + + + app:headerLayout="@layout/drawer_header" + app:menu="@menu/drawer_menu"/> @@ -56,49 +56,4 @@ - - - - - - - - - - \ No newline at end of file diff --git a/res/layout/drawer_header.xml b/res/layout/drawer_header.xml new file mode 100644 index 00000000000..40b06098224 --- /dev/null +++ b/res/layout/drawer_header.xml @@ -0,0 +1,50 @@ + + + + + + + \ No newline at end of file diff --git a/res/layout/files.xml b/res/layout/files.xml index aff6d1f1ae7..a74dbade412 100644 --- a/res/layout/files.xml +++ b/res/layout/files.xml @@ -18,35 +18,47 @@ along with this program. If not, see . --> + android:filterTouchesWhenObscured="true" + android:fitsSystemWindows="true" + android:clickable="true" > - android:orientation="horizontal" - android:id="@+id/ListLayout" - android:contentDescription="@string/list_layout" - > + - + android:background="@color/background_color" + + android:orientation="horizontal" + android:id="@+id/ListLayout" + android:contentDescription="@string/list_layout" + > + + + + + - + android:filterTouchesWhenObscured="true" + android:orientation="vertical" > - + + + + + + + + + \ No newline at end of file diff --git a/res/layout/upload_files_layout.xml b/res/layout/upload_files_layout.xml index 1c6d80c8450..3629784127d 100644 --- a/res/layout/upload_files_layout.xml +++ b/res/layout/upload_files_layout.xml @@ -17,12 +17,15 @@ along with this program. If not, see . --> + android:filterTouchesWhenObscured="true" + android:orientation="vertical" > + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/values-v21/styles.xml b/res/values-v21/styles.xml index 885c159bb29..c312c3856ba 100644 --- a/res/values-v21/styles.xml +++ b/res/values-v21/styles.xml @@ -17,12 +17,19 @@ along with this program. If not, see . --> - - + + \ No newline at end of file diff --git a/res/values/styles.xml b/res/values/styles.xml index 2a6716db1eb..5b755d29d0f 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -36,7 +36,7 @@ - - diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java new file mode 100644 index 00000000000..c4d91db823f --- /dev/null +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -0,0 +1,231 @@ +package com.owncloud.android.ui.activity; + +import android.accounts.Account; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Bundle; +import android.support.design.widget.NavigationView; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarDrawerToggle; +import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; + +import com.owncloud.android.R; +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.lib.common.OwnCloudAccount; +import com.owncloud.android.lib.common.utils.Log_OC; + +/** + * Base class to handle setup of the drawer implementation. + */ +public abstract class DrawerActivity extends ToolbarActivity { + + private static final String TAG = DrawerActivity.class.getSimpleName(); + + // Navigation Drawer + protected DrawerLayout mDrawerLayout; + protected ActionBarDrawerToggle mDrawerToggle; + + /** + * Initializes the drawer and its content. This method needs to be called after the content view has been set. + */ + protected void setupDrawer() { + mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + + NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); + if (navigationView != null) { + setupDrawerContent(navigationView); + } + + // TODO re-enable when "Accounts" is available in Navigation Drawer +// // load Account in the Drawer Title +// // User-Icon +// ImageView userIcon = (ImageView) navigationDrawerLayout.findViewById(R.id.drawer_userIcon); +// userIcon.setImageResource(DisplayUtils.getSeasonalIconId()); +// +// // Username +// TextView username = (TextView) navigationDrawerLayout.findViewById(R.id.drawer_username); +// Account account = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()); +// +// if (account != null) { +// int lastAtPos = account.name.lastIndexOf("@"); +// username.setText(account.name.substring(0, lastAtPos)); +// } +/* + // Display username in drawer + setUsernameInDrawer(navigationDrawerLayout, AccountUtils.getCurrentOwnCloudAccount(getApplicationContext())); + + // load slide menu items + mDrawerTitles = getResources().getStringArray(R.array.drawer_items); + + // nav drawer content description from resources + mDrawerContentDescriptions = getResources(). + getStringArray(R.array.drawer_content_descriptions); + + // nav drawer items + mDrawerItems = new ArrayList(); + // adding nav drawer items to array + // TODO re-enable when "Accounts" is available in Navigation Drawer + // Accounts + // mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[0], + // mDrawerContentDescriptions[0])); + // All Files + mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[0], mDrawerContentDescriptions[0], + R.drawable.ic_folder_open)); + + // TODO Enable when "On Device" is recovered + // On Device + //mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[2], + // mDrawerContentDescriptions[2])); +*/ + mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) { + + /** Called when a drawer has settled in a completely closed state. */ + public void onDrawerClosed(View view) { + super.onDrawerClosed(view); + updateActionBarTitleAndHomeButton(null); + invalidateOptionsMenu(); + } + + /** Called when a drawer has settled in a completely open state. */ + public void onDrawerOpened(View drawerView) { + super.onDrawerOpened(drawerView); + getSupportActionBar().setTitle(R.string.app_name); + mDrawerToggle.setDrawerIndicatorEnabled(true); + invalidateOptionsMenu(); + } + }; + /* + // Set the list's click listener + mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); +*/ + // Set the drawer toggle as the DrawerListener + mDrawerLayout.setDrawerListener(mDrawerToggle); + mDrawerToggle.setDrawerIndicatorEnabled(false); + } + + /** + * setup drawer content, basically setting the item selected listener. + * + * @param navigationView the drawers navigation view + */ + protected void setupDrawerContent(NavigationView navigationView) { + navigationView.setNavigationItemSelectedListener( + new NavigationView.OnNavigationItemSelectedListener() { + @Override + public boolean onNavigationItemSelected(MenuItem menuItem) { + mDrawerLayout.closeDrawers(); + + switch (menuItem.getItemId()) { + case R.id.nav_all_files: + menuItem.setChecked(true); + allFilesOption(); + break; + case R.id.nav_settings: + Intent settingsIntent = new Intent(getApplicationContext(), + Preferences.class); + startActivity(settingsIntent); + break; + } + + return true; + } + }); + } + + /** + * checks if the drawer exists and is opened. + * + * @return true if the drawer is open, else false + */ + public boolean isDrawerOpen() { + if(mDrawerLayout != null) { + return mDrawerLayout.isDrawerOpen(GravityCompat.START); + } else { + return false; + } + } + + /** + * closes the navigation drawer. + */ + public void closeNavDrawer() { + if(mDrawerLayout != null) { + mDrawerLayout.closeDrawer(GravityCompat.START); + } + } + + /** + * Method that gets called on drawer menu click for 'All Files'. + */ + public abstract void allFilesOption(); + + /** + * Updates title bar and home buttons (state and icon). + *

+ * Assumes that navigation drawer is NOT visible. + */ + protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { + super.updateActionBarTitleAndHomeButton(chosenFile); + + /// set home button properties + mDrawerToggle.setDrawerIndicatorEnabled(isRoot(chosenFile)); + } + + /** + * sets the given account name in the drawer in case the drawer is available. The account name is shortened + * beginning from the @-sign in the username. + * + * @param account the account to be set in the drawer + */ + protected void setUsernameInDrawer(Account account) { + if (mDrawerLayout != null && account != null) { + TextView username = (TextView) ((NavigationView) findViewById(R.id.nav_view)) + .getHeaderView(0).findViewById(R.id.drawer_username); + try { + OwnCloudAccount oca = new OwnCloudAccount(account, this); + username.setText(oca.getDisplayName()); + + } catch (Exception e) { + Log_OC.w(TAG, "Couldn't read display name of account; using account name instead"); + + int lastAtPos = account.name.lastIndexOf("@"); + username.setText(account.name.substring(0, lastAtPos)); + } + } + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + // Sync the toggle state after onRestoreInstanceState has occurred. + if (mDrawerToggle != null) { + mDrawerToggle.syncState(); + if (isDrawerOpen()) { + getSupportActionBar().setTitle(R.string.app_name); + mDrawerToggle.setDrawerIndicatorEnabled(true); + } + } + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (mDrawerToggle != null) { + mDrawerToggle.onConfigurationChanged(newConfig); + } + } + + @Override + public void onBackPressed() { + if (isDrawerOpen()) { + closeNavDrawer(); + return; + } + super.onBackPressed(); + } +} diff --git a/src/com/owncloud/android/ui/activity/FileActivity.java b/src/com/owncloud/android/ui/activity/FileActivity.java index 5f6b965e272..225e41079f8 100644 --- a/src/com/owncloud/android/ui/activity/FileActivity.java +++ b/src/com/owncloud/android/ui/activity/FileActivity.java @@ -35,6 +35,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.support.design.widget.NavigationView; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; @@ -43,6 +44,7 @@ import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; +import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; @@ -95,7 +97,7 @@ * Activity with common behaviour for activities handling {@link OCFile}s in ownCloud * {@link Account}s . */ -public class FileActivity extends AppCompatActivity +public class FileActivity extends DrawerActivity implements OnRemoteOperationListener, ComponentsGetter, SslUntrustedCertDialog.OnSslUntrustedCertListener { public static final String EXTRA_FILE = "com.owncloud.android.ui.activity.FILE"; @@ -160,21 +162,6 @@ public class FileActivity extends AppCompatActivity protected FileUploaderBinder mUploaderBinder = null; private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null; - // Navigation Drawer - protected DrawerLayout mDrawerLayout; - protected ActionBarDrawerToggle mDrawerToggle; - protected ListView mDrawerList; - - // Slide menu items - protected String[] mDrawerTitles; - protected String[] mDrawerContentDescriptions; - - protected ArrayList mDrawerItems; - - protected NavigationDrawerListAdapter mNavigationDrawerAdapter = null; - - - // TODO re-enable when "Accounts" is available in Navigation Drawer // protected boolean mShowAccounts = false; @@ -301,216 +288,6 @@ protected void onDestroy() { super.onDestroy(); } - @Override - protected void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - // Sync the toggle state after onRestoreInstanceState has occurred. - if (mDrawerToggle != null) { - mDrawerToggle.syncState(); - if (isDrawerOpen()) { - getSupportActionBar().setTitle(R.string.app_name); - mDrawerToggle.setDrawerIndicatorEnabled(true); - } - } - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - if (mDrawerToggle != null) { - mDrawerToggle.onConfigurationChanged(newConfig); - } - } - - @Override - public void onBackPressed() { - if (isDrawerOpen()) { - closeNavDrawer(); - return; - } - super.onBackPressed(); - } - - /** - * checks if the drawer exists and is opened. - * - * @return true if the drawer is open, else false - */ - public boolean isDrawerOpen() { - if(mDrawerLayout != null) { - return mDrawerLayout.isDrawerOpen(GravityCompat.START); - } else { - return false; - } - } - - /** - * closes the navigation drawer. - */ - public void closeNavDrawer() { - if(mDrawerLayout != null) { - mDrawerLayout.closeDrawer(GravityCompat.START); - } - } - - protected void initDrawer(){ - // constant settings for action bar when navigation drawer is inited - getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); - - - mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); - // Notification Drawer - RelativeLayout navigationDrawerLayout = (RelativeLayout) findViewById(R.id.left_drawer); - mDrawerList = (ListView) navigationDrawerLayout.findViewById(R.id.drawer_list); - - // TODO re-enable when "Accounts" is available in Navigation Drawer -// // load Account in the Drawer Title -// // User-Icon -// ImageView userIcon = (ImageView) navigationDrawerLayout.findViewById(R.id.drawer_userIcon); -// userIcon.setImageResource(DisplayUtils.getSeasonalIconId()); -// -// // Username -// TextView username = (TextView) navigationDrawerLayout.findViewById(R.id.drawer_username); -// Account account = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()); -// -// if (account != null) { -// int lastAtPos = account.name.lastIndexOf("@"); -// username.setText(account.name.substring(0, lastAtPos)); -// } - - // Display username in drawer - setUsernameInDrawer(navigationDrawerLayout, AccountUtils.getCurrentOwnCloudAccount(getApplicationContext())); - - // load slide menu items - mDrawerTitles = getResources().getStringArray(R.array.drawer_items); - - // nav drawer content description from resources - mDrawerContentDescriptions = getResources(). - getStringArray(R.array.drawer_content_descriptions); - - // nav drawer items - mDrawerItems = new ArrayList(); - // adding nav drawer items to array - // TODO re-enable when "Accounts" is available in Navigation Drawer - // Accounts - // mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[0], - // mDrawerContentDescriptions[0])); - // All Files - mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[0], mDrawerContentDescriptions[0], - R.drawable.ic_folder_open)); - - // TODO Enable when "On Device" is recovered - // On Device - //mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[2], - // mDrawerContentDescriptions[2])); - - // Uploads - mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[1], mDrawerContentDescriptions[2], - R.drawable.ic_uploads)); - - // Settings - mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[2], mDrawerContentDescriptions[1], - R.drawable.ic_settings)); - - // setting the nav drawer list adapter - mNavigationDrawerAdapter = new NavigationDrawerListAdapter(getApplicationContext(), this, - mDrawerItems); - mDrawerList.setAdapter(mNavigationDrawerAdapter); - - - mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,R.string.drawer_open,R.string.drawer_close) { - - - /** Called when a drawer has settled in a completely closed state. */ - public void onDrawerClosed(View view) { - super.onDrawerClosed(view); - updateActionBarTitleAndHomeButton(null); - invalidateOptionsMenu(); - } - - /** Called when a drawer has settled in a completely open state. */ - public void onDrawerOpened(View drawerView) { - super.onDrawerOpened(drawerView); - getSupportActionBar().setTitle(R.string.app_name); - mDrawerToggle.setDrawerIndicatorEnabled(true); - invalidateOptionsMenu(); - } - }; - - // Set the list's click listener - mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); - - // Set the drawer toggle as the DrawerListener - mDrawerLayout.setDrawerListener(mDrawerToggle); - mDrawerToggle.setDrawerIndicatorEnabled(false); - } - - /** - * sets the given account name in the drawer in case the drawer is available. The account name - * is shortened beginning from the @-sign in the username. - * - * @param navigationDrawerLayout the drawer layout to be used - * @param account the account to be set in the drawer - */ - protected void setUsernameInDrawer(View navigationDrawerLayout, Account account) { - if (navigationDrawerLayout != null && account != null) { - TextView username = (TextView) navigationDrawerLayout.findViewById(R.id.drawer_username); - try { - OwnCloudAccount oca = new OwnCloudAccount(account, this); - username.setText(oca.getDisplayName()); - - } catch (Exception e) { - Log_OC.w(TAG, "Couldn't read display name of account; using account name instead"); - - int lastAtPos = account.name.lastIndexOf("@"); - username.setText(account.name.substring(0, lastAtPos)); - } - } - } - - /** - * Updates title bar and home buttons (state and icon). - * - * Assumes that navigation drawer is NOT visible. - */ - protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { - String title = getDefaultTitle(); // default - boolean inRoot; - - /// choose the appropiate title - if (chosenFile == null) { - chosenFile = mFile; // if no file is passed, current file decides - } - inRoot = ( - chosenFile == null || - (chosenFile.isFolder() && chosenFile.getParentId() == FileDataStorageManager.ROOT_PARENT_ID) - ); - if (!inRoot) { - title = chosenFile.getFileName(); - } - - /// set the chosen title - ActionBar actionBar = getSupportActionBar(); - actionBar.setTitle(title); - /// also as content description - View actionBarTitleView = getWindow().getDecorView().findViewById( - getResources().getIdentifier("action_bar_title", "id", "android") - ); - if (actionBarTitleView != null) { // it's null in Android 2.x - actionBarTitleView.setContentDescription(title); - } - - /// set home button properties - mDrawerToggle.setDrawerIndicatorEnabled(inRoot); - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setDisplayShowTitleEnabled(true); - - } - - protected String getDefaultTitle() { - return getString(R.string.default_display_name_for_root_folder); - } - /** * Sets and validates the ownCloud {@link Account} associated to the Activity. * @@ -999,6 +776,7 @@ public void restart(){ startActivity(i); } + @Override public void allFilesOption(){ restart(); } diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index 574a0ea89a9..77fcb94b861 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -41,6 +41,8 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcelable; +import android.preference.PreferenceManager; +import android.support.design.widget.NavigationView; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; @@ -48,6 +50,7 @@ import android.support.v4.content.ContextCompat; import android.support.v4.view.GravityCompat; import android.support.v7.app.AlertDialog; +import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -167,8 +170,11 @@ protected void onCreate(Bundle savedInstanceState) { // Inflate and set the layout view setContentView(R.layout.files); - // Navigation Drawer - initDrawer(); + // setup toolbar + setupToolbar(); + + // setup drawer + setupDrawer(); mDualPane = getResources().getBoolean(R.bool.large_land_layout); mLeftFragmentContainer = findViewById(R.id.left_fragment_container); @@ -227,6 +233,9 @@ public void onClick(View v) { createMinFragments(); } + setIndeterminate(mSyncInProgress); + // always AFTER setContentView(...) in onCreate(); to work around bug in its implementation + setBackgroundText(); } @@ -302,7 +311,7 @@ protected void onAccountSet(boolean stateWasRecovered) { setFile(file); if (mAccountWasSet) { - setUsernameInDrawer(findViewById(R.id.left_drawer), getAccount()); + setUsernameInDrawer(getAccount()); } if (!stateWasRecovered) { @@ -500,7 +509,9 @@ public boolean onCreateOptionsMenu(Menu menu) { // Prevent tapjacking View actionBarView = findViewById(R.id.action_bar); - actionBarView.setFilterTouchesWhenObscured(true); + if (actionBarView != null) { + actionBarView.setFilterTouchesWhenObscured(true); + } inflater.inflate(R.menu.main_menu, menu); menu.findItem(R.id.action_create_dir).setVisible(false); @@ -808,8 +819,9 @@ protected void onSaveInstanceState(Bundle outState) { protected void onResume() { Log_OC.v(TAG, "onResume() start"); super.onResume(); + //TODO re-enable account list // refresh Navigation Drawer account list - mNavigationDrawerAdapter.updateAccountList(); + //mNavigationDrawerAdapter.updateAccountList(); // refresh list of files refreshListOfFilesFragment(true); @@ -965,7 +977,7 @@ public void onReceive(Context context, Intent intent) { } if (synchFolderRemotePath.equals(OCFile.ROOT_PATH)) { - setUsernameInDrawer(mDrawerLayout, getAccount()); + setUsernameInDrawer(getAccount()); } } @@ -976,9 +988,10 @@ public void onReceive(Context context, Intent intent) { if (fileListFragment != null) { fileListFragment.setProgressBarAsIndeterminate(mSyncInProgress); } + Log_OC.d(TAG, "Setting progress visibility to " + mSyncInProgress); + setIndeterminate(mSyncInProgress); setBackgroundText(); - } if (synchResult != null) { @@ -1071,6 +1084,8 @@ public void onReceive(Context context, Intent intent) { } } + setIndeterminate(false); + } finally { if (intent != null) { removeStickyBroadcast(intent); @@ -1284,6 +1299,9 @@ public void showDetails(OCFile file) { @Override protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { + if (chosenFile == null) { + chosenFile = getFile(); // if no file is passed, current file decides + } if (mDualPane) { // in dual pane mode, keep the focus of title an action bar in the current folder super.updateActionBarTitleAndHomeButton(getCurrentDir()); @@ -1628,6 +1646,7 @@ public void run() { if (fileListFragment != null) { fileListFragment.setProgressBarAsIndeterminate(true); } + setIndeterminate(true); setBackgroundText(); } // else: NOTHING ; lets' not refresh when the user rotates the device but there is diff --git a/src/com/owncloud/android/ui/activity/FolderPickerActivity.java b/src/com/owncloud/android/ui/activity/FolderPickerActivity.java index 1b65bdc3ef8..7c7bdd49b44 100644 --- a/src/com/owncloud/android/ui/activity/FolderPickerActivity.java +++ b/src/com/owncloud/android/ui/activity/FolderPickerActivity.java @@ -74,8 +74,6 @@ public class FolderPickerActivity extends FileActivity implements FileFragment.C protected Button mCancelBtn; protected Button mChooseBtn; - private ProgressBar mProgressBar; - @Override protected void onCreate(Bundle savedInstanceState) { @@ -93,15 +91,10 @@ protected void onCreate(Bundle savedInstanceState) { initControls(); // Action bar setup - ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); - - mProgressBar = (ProgressBar) findViewById(R.id.progressBar); - mProgressBar.setIndeterminateDrawable( - getResources().getDrawable( - R.drawable.actionbar_progress_indeterminate_horizontal)); - mProgressBar.setIndeterminate(mSyncInProgress); + setupToolbar(); + getSupportActionBar().setDisplayShowTitleEnabled(true); + + setIndeterminate(mSyncInProgress); // always AFTER setContentView(...) ; to work around bug in its implementation // sets message for empty list of folders @@ -214,7 +207,7 @@ public void startSyncFolderOperation(OCFile folder, boolean ignoreETag) { ); synchFolderOp.execute(getAccount(), this, null, null); - mProgressBar.setIndeterminate(true); + setIndeterminate(true); setBackgroundText(); } @@ -495,7 +488,7 @@ public void onReceive(Context context, Intent intent) { removeStickyBroadcast(intent); Log_OC.d(TAG, "Setting progress visibility to " + mSyncInProgress); - mProgressBar.setIndeterminate(mSyncInProgress); + setIndeterminate(mSyncInProgress); setBackgroundText(); } diff --git a/src/com/owncloud/android/ui/activity/ToolbarActivity.java b/src/com/owncloud/android/ui/activity/ToolbarActivity.java new file mode 100644 index 00000000000..c24b7b18e38 --- /dev/null +++ b/src/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -0,0 +1,92 @@ +package com.owncloud.android.ui.activity; + +import android.os.Bundle; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.widget.ProgressBar; + +import com.owncloud.android.R; +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; + +/** + * Base class providing toolbar registration functionality, see {@link #setupToolbar()}. + */ +public class ToolbarActivity extends AppCompatActivity { + private ProgressBar mProgressBar; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + /** + * Toolbar setup that must be called in implementer's {@link #onCreate} after {@link #setContentView} if they + * want to use the toolbar. + */ + protected void setupToolbar() { + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + mProgressBar = (ProgressBar) findViewById(R.id.progressBar); + mProgressBar.setIndeterminateDrawable( + ContextCompat.getDrawable(this, R.drawable.actionbar_progress_indeterminate_horizontal)); + } + + /** + * Updates title bar and home buttons (state and icon). + */ + protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { + String title = getString(R.string.default_display_name_for_root_folder); // default + boolean inRoot; + + // choose the appropiate title + inRoot = ( + chosenFile == null || + (chosenFile.isFolder() && chosenFile.getParentId() == FileDataStorageManager.ROOT_PARENT_ID) + ); + if (!inRoot) { + title = chosenFile.getFileName(); + } + + /// set the chosen title + ActionBar actionBar = getSupportActionBar(); + actionBar.setTitle(title); + + /// also as content description + View actionBarTitleView = getWindow().getDecorView().findViewById( + getResources().getIdentifier("action_bar_title", "id", "android") + ); + // TODO remove legacy code + if (actionBarTitleView != null) { // it's null in Android 2.x + actionBarTitleView.setContentDescription(title); + } + + /// set home button properties + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setDisplayShowTitleEnabled(true); + } + + /** + * checks if the given file is the root folder. + * + * @param file file to be checked if it is the root folder + * @return true if it is null or the root folder, else returns false + */ + public boolean isRoot(OCFile file) { + return file == null || + (file.isFolder() && file.getParentId() == FileDataStorageManager.ROOT_PARENT_ID); + } + + /** + * Change the indeterminate mode for the toolbar's progress bar. + * + * @param indeterminate true to enable the indeterminate mode + */ + public void setIndeterminate(boolean indeterminate) { + mProgressBar.setIndeterminate(indeterminate); + } +} diff --git a/src/com/owncloud/android/ui/activity/UploadFilesActivity.java b/src/com/owncloud/android/ui/activity/UploadFilesActivity.java index 19aaf3757dc..566ab795cd2 100644 --- a/src/com/owncloud/android/ui/activity/UploadFilesActivity.java +++ b/src/com/owncloud/android/ui/activity/UploadFilesActivity.java @@ -114,6 +114,7 @@ public void onCreate(Bundle savedInstanceState) { // Inflate and set the layout view setContentView(R.layout.upload_files_layout); + mFileListFragment = (LocalFileListFragment) getSupportFragmentManager().findFragmentById(R.id.local_files_list); @@ -138,7 +139,9 @@ public void onCreate(Bundle savedInstanceState) { if (localBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY){ mRadioBtnCopyFiles.setChecked(true); } - + + // setup the toolbar + setupToolbar(); // Action bar setup ActionBar actionBar = getSupportActionBar(); diff --git a/src/com/owncloud/android/ui/activity/UploadListActivity.java b/src/com/owncloud/android/ui/activity/UploadListActivity.java index 1dbc63c10f0..d77bcbff58c 100755 --- a/src/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/src/com/owncloud/android/ui/activity/UploadListActivity.java @@ -82,8 +82,11 @@ protected void onCreate(Bundle savedInstanceState) { // but that's other story setFile(null); - // Navigation Drawer - initDrawer(); + // setup toolbar + setupToolbar(); + + // setup drawer + setupDrawer(); // Add fragment with a transaction for setting a tag if(savedInstanceState == null) { @@ -340,7 +343,6 @@ public void onReceive(Context context, Intent intent) { } } - @Override protected String getDefaultTitle() { return getString(R.string.uploads_view_title); } @@ -354,7 +356,7 @@ protected void onAccountSet(boolean stateWasRecovered) { super.onAccountSet(stateWasRecovered); updateActionBarTitleAndHomeButton(null); if (mAccountWasSet) { - setUsernameInDrawer(findViewById(R.id.left_drawer), getAccount()); + setUsernameInDrawer(getAccount()); } } diff --git a/src/com/owncloud/android/ui/preview/PreviewImageActivity.java b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java index bdf409877ae..e8746d97aa1 100644 --- a/src/com/owncloud/android/ui/preview/PreviewImageActivity.java +++ b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java @@ -89,7 +89,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.preview_image_activity); // Navigation Drawer - initDrawer(); + setupDrawer(); // ActionBar ActionBar actionBar = getSupportActionBar(); From 271328e82aeb262900b4d4db6025f9cedc34c240 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Wed, 23 Mar 2016 15:57:10 +0100 Subject: [PATCH 02/84] navigation bug fix for previewing text files --- res/layout/preview_audio_fragment.xml | 1 - res/layout/preview_text_fragment.xml | 1 - res/layout/preview_video_fragment.xml | 1 - .../owncloud/android/ui/activity/FileDisplayActivity.java | 6 ------ 4 files changed, 9 deletions(-) diff --git a/res/layout/preview_audio_fragment.xml b/res/layout/preview_audio_fragment.xml index 8da0200d73f..60f5ce2a353 100644 --- a/res/layout/preview_audio_fragment.xml +++ b/res/layout/preview_audio_fragment.xml @@ -24,7 +24,6 @@ android:id="@+id/top" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background_color" android:gravity="center" tools:context=".ui.preview.PreviewAudioFragment" android:filterTouchesWhenObscured="true"> diff --git a/res/layout/preview_text_fragment.xml b/res/layout/preview_text_fragment.xml index 90acf001bce..530c0f96610 100644 --- a/res/layout/preview_text_fragment.xml +++ b/res/layout/preview_text_fragment.xml @@ -22,7 +22,6 @@ android:id="@+id/top" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background_color" android:gravity="center" tools:context=".ui.preview.PreviewAudioFragment" android:filterTouchesWhenObscured="true" diff --git a/res/layout/preview_video_fragment.xml b/res/layout/preview_video_fragment.xml index 1ce5e2c561e..3c6436b1367 100644 --- a/res/layout/preview_video_fragment.xml +++ b/res/layout/preview_video_fragment.xml @@ -24,7 +24,6 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background_color" android:gravity="center" tools:context=".ui.preview.PreviewAudioFragment" android:filterTouchesWhenObscured="true" diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index 77fcb94b861..7067e97f00d 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -181,12 +181,6 @@ protected void onCreate(Bundle savedInstanceState) { mRightFragmentContainer = findViewById(R.id.right_fragment_container); // Action bar setup - getSupportActionBar().setHomeButtonEnabled(true); // mandatory since Android ICS, - // according to the official - // documentation - - // enable ActionBar app icon to behave as action to toggle nav drawer - //getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setHomeButtonEnabled(true); // Init Fragment without UI to retain AsyncTask across configuration changes From 07a3e35bbee41d4c69f10128e15e8c98c233727b Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Wed, 23 Mar 2016 16:17:14 +0100 Subject: [PATCH 03/84] initial bug fix for the uploader --- res/layout/uploader_layout.xml | 48 ++++++++++++------- .../ReceiveExternalFilesActivity.java | 3 +- .../adapter/NavigationDrawerListAdapter.java | 1 + 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/res/layout/uploader_layout.xml b/res/layout/uploader_layout.xml index 92c4a3217f2..9171c25c9fe 100644 --- a/res/layout/uploader_layout.xml +++ b/res/layout/uploader_layout.xml @@ -17,27 +17,28 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - + android:orientation="vertical" > + + @@ -45,19 +46,30 @@ + + + + + + + android:padding="@dimen/standard_padding"> - + diff --git a/src/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java b/src/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java index 40daf8e39fa..4fb2f15559f 100644 --- a/src/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java +++ b/src/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java @@ -389,9 +389,10 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { private void populateDirectoryList() { setContentView(R.layout.uploader_layout); + setupToolbar(); + ActionBar actionBar = getSupportActionBar(); ListView mListView = (ListView) findViewById(android.R.id.list); - ActionBar actionBar = getSupportActionBar(); String current_dir = mParents.peek(); if (current_dir.equals("")) { diff --git a/src/com/owncloud/android/ui/adapter/NavigationDrawerListAdapter.java b/src/com/owncloud/android/ui/adapter/NavigationDrawerListAdapter.java index 93c1ea79d50..40ecb1c8de0 100644 --- a/src/com/owncloud/android/ui/adapter/NavigationDrawerListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/NavigationDrawerListAdapter.java @@ -40,6 +40,7 @@ import java.util.ArrayList; +// TODO remove complete class as soon as account switcher v2 is implemented, keep it until then for inspiration public class NavigationDrawerListAdapter extends BaseAdapter { private final static String TAG = NavigationDrawerListAdapter.class.getSimpleName(); From 25b52f1262a3f92013b7f9945ef4ed410db1e10f Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Wed, 23 Mar 2016 19:58:51 +0100 Subject: [PATCH 04/84] initial (functionless) account switcher for the drawer header and display size depending header sizing --- res/drawable-hdpi/ic_down.png | Bin 0 -> 189 bytes res/drawable-hdpi/ic_up.png | Bin 0 -> 211 bytes res/drawable-mdpi/ic_down.png | Bin 0 -> 169 bytes res/drawable-mdpi/ic_up.png | Bin 0 -> 171 bytes res/drawable-xhdpi/ic_down.png | Bin 0 -> 199 bytes res/drawable-xhdpi/ic_up.png | Bin 0 -> 205 bytes res/drawable-xxhdpi/ic_down.png | Bin 0 -> 245 bytes res/drawable-xxhdpi/ic_up.png | Bin 0 -> 239 bytes res/layout/drawer_header.xml | 97 +++++++++++++----- res/values-sw360dp/dims.xml | 22 ++++ res/values/dims.xml | 1 + res/values/strings.xml | 1 + .../android/ui/activity/DrawerActivity.java | 3 + 13 files changed, 96 insertions(+), 28 deletions(-) create mode 100644 res/drawable-hdpi/ic_down.png create mode 100644 res/drawable-hdpi/ic_up.png create mode 100644 res/drawable-mdpi/ic_down.png create mode 100644 res/drawable-mdpi/ic_up.png create mode 100644 res/drawable-xhdpi/ic_down.png create mode 100644 res/drawable-xhdpi/ic_up.png create mode 100644 res/drawable-xxhdpi/ic_down.png create mode 100644 res/drawable-xxhdpi/ic_up.png create mode 100644 res/values-sw360dp/dims.xml diff --git a/res/drawable-hdpi/ic_down.png b/res/drawable-hdpi/ic_down.png new file mode 100644 index 0000000000000000000000000000000000000000..5cd8ab6613dd26c203159735dac56f418c493389 GIT binary patch literal 189 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbB*pj^6U4S$Y{B+)352QE?JR*yM zvQ&rUPlPeufHm>3)!`r;B4q#NoH+4)QVx2rxJ^<)j^xvrY%735fT@xp&c&_DA;O2KY Tb)JPCXeNWFtDnm{r-UW|=FT-l literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_up.png b/res/drawable-hdpi/ic_up.png new file mode 100644 index 0000000000000000000000000000000000000000..a17476a712aab040f720494fa585adf6540334ef GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbB*pj^6U4S$Y{B+)352QE?JR*yM zvQ&rUPlPeufHm>3$$5&eO#)B;xSf3kNwH3=a#f`ToN>M?OXYZDR0r^>bP0l+XkKl8Z+n literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_down.png b/res/drawable-mdpi/ic_down.png new file mode 100644 index 0000000000000000000000000000000000000000..23d8de8107099ccd5ca40e4ee194014478ad4921 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjY)RhkE(}6TtKSPDj(q%x7iKmNWh{y5d1PRu~2_ihjAO8RU-}b-7ky%%F x;h6%aX#vLzWTpjNFEH`WNdBP#RMb$zz%VDhXtKjW{|P|-44$rjF6*2UngCRFExP~! literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_up.png b/res/drawable-mdpi/ic_up.png new file mode 100644 index 0000000000000000000000000000000000000000..b932a12a9d0849a5e06b3c23e31db97a454fa3cf GIT binary patch literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjY)RhkE(}6TtKSPDj(q%x7nWu|mh{y5d1PRu~2_iiPAO8RU{|kt(U%pfz zv0$BQMqBW8A(^(|<3dbrm(_N-9n_FuUBb-3`M;=hVR`Hvpa~3~u6{1-oD!Mft0H%Rn57q?SH4!-lWjHu2{+a&m)(0KTdYe`Dm|Rbevx;>qx!Nm9BcFVdQ&MBb@0Q7b{{{R30 literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_up.png b/res/drawable-xhdpi/ic_up.png new file mode 100644 index 0000000000000000000000000000000000000000..c2a8163d51c0aa3af7e2dc4bf127fc08b2fa022a GIT binary patch literal 205 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCwj^(N7a$D;Kb?2i11Zh|kH}&m z?E%JaC$sH9f@KAc=|CE+pW)oQo^T*1!qdeuB;(%Oi;i3e6a-it>)%~AUmnAB(1o>p z;VV{KpKPF-17(62l^4x_7ty)@s?^7h^`?(h_Z{nSta&FW|G2pChvfY1BPlg~g54j} i-E+Fme`En_5!S2DSQ9Z{MqL}oVeoYIb6Mw<&;$U32tHH* literal 0 HcmV?d00001 diff --git a/res/drawable-xxhdpi/ic_down.png b/res/drawable-xxhdpi/ic_down.png new file mode 100644 index 0000000000000000000000000000000000000000..cacbf3e51623fe37804ce2717de0d3df67f61293 GIT binary patch literal 245 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!Y)RhkE=uJQvbkkjDl;uunK>+OY;ye$p_ERH_;=iB*zM{;&1&seRj zv*J`PGf)u-D9jAr7EM4zWEnNz6ec1rg7bCsYk_e`hk_qbSnenKCRWQUl^5VD2&&HlAL->yWQ P2Z?yP`njxgN@xNA0EAG+ literal 0 HcmV?d00001 diff --git a/res/drawable-xxhdpi/ic_up.png b/res/drawable-xxhdpi/ic_up.png new file mode 100644 index 0000000000000000000000000000000000000000..f35d9fc95dcc253262a366a9b9c0747a542eded0 GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!Y)RhkE=uJQvbkW=O9;uunK>+OZxoDBv73r&3Pz?w)XbBp+9{EywTwk|1OfC2Avl&}T4URAAvfQa2dhVp<%O`VUChMI$Y54Nl zoQ>+Hvy7iDIaYjZ)|U%&4ApFHmcRGZKl`%y(&^$~b66neIfxmTwXDq(a+3WG67Y2O Kb6Mw<&;$SwqffH{ literal 0 HcmV?d00001 diff --git a/res/layout/drawer_header.xml b/res/layout/drawer_header.xml index 40b06098224..ddea4feb73b 100644 --- a/res/layout/drawer_header.xml +++ b/res/layout/drawer_header.xml @@ -16,35 +16,76 @@ along with this program. If not, see . --> - - + android:layout_width="match_parent" + android:layout_height="@dimen/nav_drawer_header_height" + android:paddingBottom="@dimen/standard_padding" + android:paddingRight="@dimen/standard_padding" + android:paddingLeft="@dimen/standard_padding" + android:background="@color/drawer_header_color"> - + > + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/values-sw360dp/dims.xml b/res/values-sw360dp/dims.xml new file mode 100644 index 00000000000..133a697826b --- /dev/null +++ b/res/values-sw360dp/dims.xml @@ -0,0 +1,22 @@ + + + + + 164dp + diff --git a/res/values/dims.xml b/res/values/dims.xml index dbc6a3c01c7..7e27ae79387 100644 --- a/res/values/dims.xml +++ b/res/values/dims.xml @@ -21,6 +21,7 @@ @dimen/standard_padding @dimen/standard_padding 260dp + 140dp 32dp 128dp diff --git a/res/values/strings.xml b/res/values/strings.xml index 87b5ad1852c..6b157644fbe 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -345,6 +345,7 @@ Accounts Add account + Manage accounts Secure connection is redirected through an unsecured route. Logs diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index c4d91db823f..007277d73bc 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -186,6 +186,9 @@ protected void setUsernameInDrawer(Account account) { if (mDrawerLayout != null && account != null) { TextView username = (TextView) ((NavigationView) findViewById(R.id.nav_view)) .getHeaderView(0).findViewById(R.id.drawer_username); + TextView usernameFull = (TextView) ((NavigationView) findViewById(R.id.nav_view)) + .getHeaderView(0).findViewById(R.id.drawer_username_full); + usernameFull.setText(account.name); try { OwnCloudAccount oca = new OwnCloudAccount(account, this); username.setText(oca.getDisplayName()); From adebaf9fe767deab31bf5872816b3c57c717df83 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Wed, 23 Mar 2016 20:56:22 +0100 Subject: [PATCH 05/84] initial account chooser toggle and further abstraction of the drawer API for child classes --- res/layout/drawer_header.xml | 3 +- .../android/ui/activity/DrawerActivity.java | 95 ++++++++++++++++--- .../android/ui/activity/FileActivity.java | 2 + .../ui/preview/PreviewImageActivity.java | 10 +- 4 files changed, 89 insertions(+), 21 deletions(-) diff --git a/res/layout/drawer_header.xml b/res/layout/drawer_header.xml index ddea4feb73b..a94061d777a 100644 --- a/res/layout/drawer_header.xml +++ b/res/layout/drawer_header.xml @@ -24,6 +24,7 @@ android:background="@color/drawer_header_color"> true if the drawer is open, else false */ public boolean isDrawerOpen() { - if(mDrawerLayout != null) { - return mDrawerLayout.isDrawerOpen(GravityCompat.START); - } else { - return false; - } + return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START); } /** - * closes the navigation drawer. + * closes the drawer. */ - public void closeNavDrawer() { - if(mDrawerLayout != null) { + public void closeDrawer() { + if (mDrawerLayout != null) { mDrawerLayout.closeDrawer(GravityCompat.START); } } + /** + * opens the drawer. + */ + public void openDrawer() { + if (mDrawerLayout != null) { + mDrawerLayout.openDrawer(GravityCompat.START); + } + } + + /** + * Enable or disable interaction with all drawers. + * + * @param lockMode The new lock mode for the given drawer. One of {@link DrawerLayout#LOCK_MODE_UNLOCKED}, + * {@link DrawerLayout#LOCK_MODE_LOCKED_CLOSED} or {@link DrawerLayout#LOCK_MODE_LOCKED_OPEN}. + */ + public void setDrawerLockMode(int lockMode) { + if (mDrawerLayout != null) { + mDrawerLayout.setDrawerLockMode(lockMode); + } + } + + /** + * Enable or disable the drawer indicator. + * + * @param enable true to enable, false to disable + */ + public void setDrawerIndicatorEnabled(boolean enable) { + if (mDrawerToggle != null) { + mDrawerToggle.setDrawerIndicatorEnabled(enable); + } + } + /** * Method that gets called on drawer menu click for 'All Files'. */ @@ -180,7 +220,7 @@ protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { * sets the given account name in the drawer in case the drawer is available. The account name is shortened * beginning from the @-sign in the username. * - * @param account the account to be set in the drawer + * @param account the account to be set in the drawer */ protected void setUsernameInDrawer(Account account) { if (mDrawerLayout != null && account != null) { @@ -202,6 +242,21 @@ protected void setUsernameInDrawer(Account account) { } } + /** + * Toggle between drawer menu and account list. + */ + private void toggleAccountList() { + if (mIsAccountChooserActive) { + // TODO close accounts list and display drawer menu again + mAccountChooserToggle.setImageResource(R.drawable.ic_down); + } else { + // TODO show accounts list + mAccountChooserToggle.setImageResource(R.drawable.ic_up); + } + + mIsAccountChooserActive = !mIsAccountChooserActive; + } + @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); @@ -226,9 +281,19 @@ public void onConfigurationChanged(Configuration newConfig) { @Override public void onBackPressed() { if (isDrawerOpen()) { - closeNavDrawer(); + closeDrawer(); return; } super.onBackPressed(); } + + /** + * Finds a view that was identified by the id attribute from the drawer header. + * + * @param id the view's id + * @return The view if found or null otherwise. + */ + private View findNavigationViewChildById(int id) { + return ((NavigationView) findViewById(R.id.nav_view)).getHeaderView(0).findViewById(id); + } } diff --git a/src/com/owncloud/android/ui/activity/FileActivity.java b/src/com/owncloud/android/ui/activity/FileActivity.java index 225e41079f8..e9870ff0a25 100644 --- a/src/com/owncloud/android/ui/activity/FileActivity.java +++ b/src/com/owncloud/android/ui/activity/FileActivity.java @@ -831,6 +831,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) case 0: // All Files allFilesOption(); + //mDrawerLayout.closeDrawers(); break; // TODO Enable when "On Device" is recovered ? @@ -849,6 +850,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) Intent settingsIntent = new Intent(getApplicationContext(), Preferences.class); startActivity(settingsIntent); + //mDrawerLayout.closeDrawers(); break; } mDrawerLayout.closeDrawers(); diff --git a/src/com/owncloud/android/ui/preview/PreviewImageActivity.java b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java index e8746d97aa1..9c5d52ccd50 100644 --- a/src/com/owncloud/android/ui/preview/PreviewImageActivity.java +++ b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java @@ -110,10 +110,10 @@ public void onSystemUiVisibilityChange(int flags) { ActionBar actionBar = getSupportActionBar(); if (visible) { actionBar.show(); - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); + setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); } else { actionBar.hide(); - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); + setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); } } }); @@ -271,8 +271,8 @@ public boolean onOptionsItemSelected(MenuItem item) { switch(item.getItemId()){ case android.R.id.home: - if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) { - mDrawerLayout.closeDrawer(GravityCompat.START); + if (isDrawerOpen()) { + closeDrawer(); } else { backToDisplayActivity(); } @@ -338,7 +338,7 @@ public void onPageSelected(int position) { OCFile currentFile = mPreviewImagePagerAdapter.getFileAt(position); getSupportActionBar().setTitle(currentFile.getFileName()); - mDrawerToggle.setDrawerIndicatorEnabled(false); + setDrawerIndicatorEnabled(false); if (!mPreviewImagePagerAdapter.pendingErrorAt(position)) { getFileOperationsHelper().syncFile(currentFile); } From 4efcb5c1119b68567ccd3e78e6867ed7c4d49242 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Wed, 23 Mar 2016 22:51:37 +0100 Subject: [PATCH 06/84] general menu/account list switch implemented (open: populate account list) --- res/drawable-hdpi/ic_account_plus.png | Bin 0 -> 657 bytes res/drawable-mdpi/ic_account_plus.png | Bin 0 -> 398 bytes res/drawable-xhdpi/ic_account_plus.png | Bin 0 -> 677 bytes res/drawable-xxhdpi/ic_account_plus.png | Bin 0 -> 1028 bytes res/menu/drawer_menu.xml | 31 ++++++++++++++++-- res/values-v21/styles.xml | 1 + res/values/styles.xml | 5 +++ src/com/owncloud/android/ui/TextDrawable.java | 6 ++-- .../android/ui/activity/DrawerActivity.java | 27 +++++++++++++-- 9 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 res/drawable-hdpi/ic_account_plus.png create mode 100644 res/drawable-mdpi/ic_account_plus.png create mode 100644 res/drawable-xhdpi/ic_account_plus.png create mode 100644 res/drawable-xxhdpi/ic_account_plus.png diff --git a/res/drawable-hdpi/ic_account_plus.png b/res/drawable-hdpi/ic_account_plus.png new file mode 100644 index 0000000000000000000000000000000000000000..f3fc7aa18e87ea66134d17d7575e8faebd22b3fc GIT binary patch literal 657 zcmV;C0&e|@P)=T0QJ^I)|B7s8*|;DwoTjRds2aS46tKUhl%r&d$#`PciPmb=}5j?Rj1^ z&SzQHS`rulRb8?yYYljv8c0<)$F{LRBC?XEqLDuq$fU?xh-L;-)sOS^&kPg8wA0@L=S-DIFEs+DL6KL-+waNL}UM)$y2Mqec$(AWLhDB=#o0a2k;~ffci9; zxdBB`+s2qxRow%QfB-mB)jea(YLZtPCY0E5938uCu7BP_D)iq16=w~wZTpOfTo;jR zz|}AeFNw%GpaLAL>NjJ|C*Yl`c2xCktJV6R8c0=RzY74@b=Si%d?+HffwJnb>NqD`6P!MirS!s@gHeJn%g4_v@Vs$Z?#T zz)RrttT1Ex0o?U{|4k~OdcD3}EEYd7?2UQrxZm$zZnxWC5>HvFRJxN93_vByPd@gl rdNw1NQT}o&phfAQ1Im>vSJwIk$+x<$O82zq00000NkvXXu0mjf8}KBC literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_account_plus.png b/res/drawable-mdpi/ic_account_plus.png new file mode 100644 index 0000000000000000000000000000000000000000..8db5d3198a0a02e9b22be3bedbcacd6f413cafdb GIT binary patch literal 398 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjY)RhkE(}6TtKSPDj(q#+`jFO%%jv*e$-v)2=J8U4bXZjOvD{)TFvR#IT zhFgre{|hE*&lj-F{Kp)cl$WmNRyO0rnL}O0I&NAkGhc;-PM`PW==195&#T$|EX-6w zCMl>Xhgz42O=&nM9XE1YY)PE0ji_A+ac0Q01+%PkmsWVSc* zGA|XG8uaIQ@~)%zPqUU9uq ztEWen=_;u<%$9xs$tNf@^y{VU1zYzi|CG42^6>VKJJQMXwxu}~*)6bOQ1QDh{pn}J iqxvoE^A`#C^~=AIPIESx+N}x>!AXAULRqE~1M#I7sVokoO!eEmAz1qKZ5rf{s$^If`$Qlz+O05HFiD;T-^T|%$%akI$pXssW{aeOOpz0pP7Zny6M z!vc|EmdTA#fiMg=9miP#25eo}wnG7fqW2)kagbFZ0o)FPV6BnsblH=*F6=?pG`n8F z=py!g|1fYKI0u{njsu5)eObvb;0N#-_yoKLUPV##y)l6x2s&EpRju_E;MAP*gW0Py zBmO}M@k9u*F>iOOY)5fj_p+4oE^wjZXYx`hbw5dxXGR3N-R_ZAtMwSTSaIXCyhzjZ zYQNwAR+)h3dCNkG4d8&`<|q8rTCc`&JiVTj7I0nnq?GavuvAm?6MieD&L&Cn5f<1+ zrIhy?XdYlG8=Hj$DE5C()lA1;L|}h|0@JZ~{Yh*EEEbE!Vrh~;Ezzw7#CAiz00000 LNkvXXu0mjf?fWb} literal 0 HcmV?d00001 diff --git a/res/drawable-xxhdpi/ic_account_plus.png b/res/drawable-xxhdpi/ic_account_plus.png new file mode 100644 index 0000000000000000000000000000000000000000..ba4edbd054584eb91d7d7071c67685bb0c7adb2c GIT binary patch literal 1028 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!Y)RhkE=uJQvb0|Rrvr;B4q#jUq9{WD}jWsckLzw7(X(D?#MI+BH-D{%I5y!iG=Jq4acdIf^_?RtekMlYxvu8%Plo$72(OV7bfh`Z@ zwSrbAd`xim+iY#mZ|&8&a`Vl~$(0AcH|{<2?)SU*wcp;J`@NU7|M=sN6*h7p>Wyho z8DpeG@;ug82WGGOWm8~M`)XdtAD$TIYljT}e63f_NK1Qm0tk*Duy)XW{4_Q}weNBN zwCU69H%GQlZt5{C4gD)pQ8eAz-1SL}L|gW@td-MrldT;EY8YymWMq({UE-xSy%tgR7iIiGpzoWRZwpPLb$=Qxax*(_cqeL&HBBEM^j4bS1Q6fVqN=N>pE-E?1K@@yf;igM--+3`!)UGVMxFL7(j~%`b6nKoj z?KhjOR1_C_K4E*L?R@hiouVuPZ4YY&XR--#7XtnC{Oyy2ijE8~fBH7pOcGj<_&VgG zrXxe3NUYFA&4l8PhqIN8L|recMt`+X=lG?q+4tD+fmZX~ySoltwdgyZGtb@G(48mD z^Ag34f4^+^cRVh8?$ML*Gpc(Q@W4^bp0nPYZ_0>I3~vy3P2sU%v+>>U|9f|M z^$fc@_IImZeLv8@>R0aa%QqR?-f7PjzLK@oZ^f(EtE#wWu<-F4aEiM8uD@`<%1p&Yim=L@T{vv*)BeDVF~X0>ZEETGe)JhJ3^MNh*=C zF)@Gg%pI%u?aVftz1;PQv!LbH>+g9NtlA#>HEH?forWh~fA^VSxQuU0=+~=8NB(3# zQi^AOyXM~70>|Rw;>CCKzH@yjyBQ>RSbS^L+rK}5{$#hdja;!HY;~!U=Oq2c=cl$! zIH*6>>*?CLb!WJRQ+g!BuSS1)Z}Pj5UFlPlM4NPfoKDK(8+qILSG>~t?ap^##j17N zZog#)3cFmmcJ12k11>j><(fLTe5==3b#;bb^|EbQ`d$}Kr=GtQ_STiNYSo*_TdVGt nf0nFpgd=c1fMkclzWog6mupAK#~G^w^C^R;tDnm{r-UW|Ziv_P literal 0 HcmV?d00001 diff --git a/res/menu/drawer_menu.xml b/res/menu/drawer_menu.xml index d6cd4c1a01a..8594e6390f8 100644 --- a/res/menu/drawer_menu.xml +++ b/res/menu/drawer_menu.xml @@ -17,20 +17,47 @@ along with this program. If not, see . -->

- + + - + + + + + + + diff --git a/res/values-v21/styles.xml b/res/values-v21/styles.xml index c312c3856ba..edfd58ddad3 100644 --- a/res/values-v21/styles.xml +++ b/res/values-v21/styles.xml @@ -27,6 +27,7 @@ @style/Theme.ownCloud.Dialog @style/ownCloud.AlertDialog @style/ownCloud.SearchView + @color/transparent + + + diff --git a/src/com/owncloud/android/ui/TextDrawable.java b/src/com/owncloud/android/ui/TextDrawable.java index 610e7fe546c..5102e12b142 100644 --- a/src/com/owncloud/android/ui/TextDrawable.java +++ b/src/com/owncloud/android/ui/TextDrawable.java @@ -5,7 +5,7 @@ import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; -import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.drawable.Drawable; /** @@ -35,7 +35,9 @@ public TextDrawable(String text, int r, int g, int b) { @Override public void draw(Canvas canvas) { - canvas.drawRect(0,-20,20,40,bg); + RectF rf = new RectF(0,24,24,0); + canvas.drawRoundRect(rf,24,24,bg); + //canvas.drawRect(0,-20,20,40,bg); canvas.drawText(text, 4, 6, paint); } diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index c48f9b25de1..966e8cf51ca 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -1,5 +1,7 @@ package com.owncloud.android.ui.activity; +import android.accounts.Account; +import android.accounts.AccountManager; import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; @@ -7,15 +9,19 @@ import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; +import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.ImageView; import android.widget.TextView; +import android.widget.Toast; +import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.ui.TextDrawable; /** * Base class to handle setup of the drawer implementation. @@ -27,6 +33,7 @@ public abstract class DrawerActivity extends ToolbarActivity { // Navigation Drawer private DrawerLayout mDrawerLayout; private ActionBarDrawerToggle mDrawerToggle; + private NavigationView mNavigationView; private ImageView mAccountChooserToggle; private boolean mIsAccountChooserActive; @@ -37,9 +44,9 @@ public abstract class DrawerActivity extends ToolbarActivity { protected void setupDrawer() { mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); - NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); - if (navigationView != null) { - setupDrawerContent(navigationView); + mNavigationView = (NavigationView) findViewById(R.id.nav_view); + if (mNavigationView != null) { + setupDrawerContent(mNavigationView); mAccountChooserToggle = (ImageView) findNavigationViewChildById(R.id.drawer_account_chooser_toogle); mAccountChooserToggle.setImageResource(R.drawable.ic_down); mIsAccountChooserActive = false; @@ -142,11 +149,20 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { Preferences.class); startActivity(settingsIntent); break; + case R.id.drawer_menu_account_add: + AccountManager am = AccountManager.get(getApplicationContext()); + am.addAccount(MainApp.getAccountType(), null, null, null, DrawerActivity.this, + null, null); + case R.id.drawer_menu_account_manage: + Toast.makeText(getApplicationContext(),"Not implemented yet",Toast.LENGTH_SHORT); } return true; } }); + + // hide accounts + mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false); } /** @@ -249,9 +265,14 @@ private void toggleAccountList() { if (mIsAccountChooserActive) { // TODO close accounts list and display drawer menu again mAccountChooserToggle.setImageResource(R.drawable.ic_down); + mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false); + mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, true); + } else { // TODO show accounts list mAccountChooserToggle.setImageResource(R.drawable.ic_up); + mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, true); + mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, false); } mIsAccountChooserActive = !mIsAccountChooserActive; From ac9876f4c2ec9e3120ca7e9e3ad39f9430021339 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 24 Mar 2016 00:19:06 +0100 Subject: [PATCH 07/84] initial account handling in drawer + initial icon generation (still broken) --- src/com/owncloud/android/ui/TextDrawable.java | 6 +- .../android/ui/activity/DrawerActivity.java | 74 ++++++++++++++++++- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/src/com/owncloud/android/ui/TextDrawable.java b/src/com/owncloud/android/ui/TextDrawable.java index 5102e12b142..c858a43f27d 100644 --- a/src/com/owncloud/android/ui/TextDrawable.java +++ b/src/com/owncloud/android/ui/TextDrawable.java @@ -35,9 +35,9 @@ public TextDrawable(String text, int r, int g, int b) { @Override public void draw(Canvas canvas) { - RectF rf = new RectF(0,24,24,0); - canvas.drawRoundRect(rf,24,24,bg); - //canvas.drawRect(0,-20,20,40,bg); + //RectF rf = new RectF(0,24,24,0); + //canvas.drawRoundRect(rf,24,24,bg); + canvas.drawRect(0,-20,20,40,bg); canvas.drawText(text, 4, 6, paint); } diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index 966e8cf51ca..122214882f2 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -18,10 +18,20 @@ import com.owncloud.android.MainApp; import com.owncloud.android.R; +import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.TextDrawable; +import com.owncloud.android.utils.BitmapUtils; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; /** * Base class to handle setup of the drawer implementation. @@ -36,6 +46,9 @@ public abstract class DrawerActivity extends ToolbarActivity { private NavigationView mNavigationView; private ImageView mAccountChooserToggle; + private List mAccountMenuItemIds = new ArrayList(); + private Account mCurrentAccount; + private boolean mIsAccountChooserActive; /** @@ -154,7 +167,7 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { am.addAccount(MainApp.getAccountType(), null, null, null, DrawerActivity.this, null, null); case R.id.drawer_menu_account_manage: - Toast.makeText(getApplicationContext(),"Not implemented yet",Toast.LENGTH_SHORT); + Toast.makeText(getApplicationContext(), "Not implemented yet", Toast.LENGTH_SHORT); } return true; @@ -215,6 +228,64 @@ public void setDrawerIndicatorEnabled(boolean enable) { } } + /** + * updates the account list in the drawer. + */ + // TODO call updateAccountList() after n.o. accounts changed + public void updateAccountList() { + AccountManager am = (AccountManager) this.getSystemService(this.ACCOUNT_SERVICE); + + // populate UI + repopulateAccountList(am.getAccountsByType(MainApp.getAccountType())); + setUsernameInDrawer(AccountUtils.getCurrentOwnCloudAccount(this)); + } + + /** + * re-populates the account list. + * + * @param accounts list of accounts + */ + private void repopulateAccountList(Account[] accounts) { + // remove all accounts from list + mNavigationView.getMenu().removeItem(Menu.NONE); + + // add all accounts to list + for (int i = 0; i < accounts.length; i++) { + try { + int[] rgb = calculateRGB(accounts[i]); + TextDrawable icon = new TextDrawable(accounts[i].name.substring(0, 1).toUpperCase(), + rgb[0], rgb[1], rgb[2]); + mNavigationView.getMenu().add(R.id.drawer_menu_accounts, Menu.NONE, 0, accounts[i].name).setIcon(icon); + } catch (Exception e) { + Log_OC.e(TAG, "Error calculating RGB value for account menu item.", e); + mNavigationView.getMenu().add(R.id.drawer_menu_accounts, Menu.NONE, 0, accounts[i].name).setIcon(R + .drawable.ic_account_circle); + } + } + + // adding sets menu group back to visible, so safety check and setting invisible + if (!mIsAccountChooserActive) { + mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false); + } + } + + private int[] calculateRGB(Account account) throws UnsupportedEncodingException, NoSuchAlgorithmException { + // using adapted algorithm from /core/js/placeholder.js:50 + int lastAtPos = account.name.lastIndexOf("@"); + String username = account.name.substring(0, lastAtPos); + byte[] seed = username.getBytes("UTF-8"); + MessageDigest md = MessageDigest.getInstance("MD5"); +// Integer seedMd5Int = Math.abs(new String(Hex.encodeHex(seedMd5)) +// .hashCode()); + Integer seedMd5Int = String.format(Locale.ROOT, "%032x", + new BigInteger(1, md.digest(seed))).hashCode(); + + double maxRange = Integer.MAX_VALUE; + float hue = (float) (seedMd5Int / maxRange * 360); + + return BitmapUtils.HSLtoRGB(hue, 90.0f, 65.0f, 1.0f); + } + /** * Method that gets called on drawer menu click for 'All Files'. */ @@ -289,6 +360,7 @@ protected void onPostCreate(Bundle savedInstanceState) { mDrawerToggle.setDrawerIndicatorEnabled(true); } } + updateAccountList(); } @Override From 7df97afb84a49b0714ea18f68b76ef8a4ca99edc Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 24 Mar 2016 01:28:57 +0100 Subject: [PATCH 08/84] circle icons for accounts with color coding --- res/layout/drawer_header.xml | 8 +++--- src/com/owncloud/android/ui/TextDrawable.java | 13 +++++----- .../android/ui/activity/DrawerActivity.java | 25 ++++++++++++++----- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/res/layout/drawer_header.xml b/res/layout/drawer_header.xml index a94061d777a..76471c8aeb0 100644 --- a/res/layout/drawer_header.xml +++ b/res/layout/drawer_header.xml @@ -32,17 +32,19 @@ diff --git a/src/com/owncloud/android/ui/TextDrawable.java b/src/com/owncloud/android/ui/TextDrawable.java index c858a43f27d..70dd3521f50 100644 --- a/src/com/owncloud/android/ui/TextDrawable.java +++ b/src/com/owncloud/android/ui/TextDrawable.java @@ -5,7 +5,6 @@ import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; -import android.graphics.RectF; import android.graphics.drawable.Drawable; /** @@ -24,21 +23,23 @@ public TextDrawable(String text, int r, int g, int b) { bg = new Paint(); bg.setStyle(Paint.Style.FILL); + bg.setAntiAlias(true); bg.setColor(color); paint = new Paint(); paint.setColor(Color.WHITE); - paint.setTextSize(20); + paint.setTextSize(60); paint.setAntiAlias(true); paint.setFakeBoldText(true); } @Override public void draw(Canvas canvas) { - //RectF rf = new RectF(0,24,24,0); - //canvas.drawRoundRect(rf,24,24,bg); - canvas.drawRect(0,-20,20,40,bg); - canvas.drawText(text, 4, 6, paint); + //float[] radii = {radius, radius, radius, radius, radius, radius, radius, radius}; + //new RoundRectShape(radii, null, null); + canvas.drawCircle(40, 40, 40, bg); + //canvas.drawRect(0, -20, 20, 40, bg); + canvas.drawText(text, 20, 60, paint); } @Override diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index 122214882f2..dad218799bc 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -4,6 +4,7 @@ import android.accounts.AccountManager; import android.content.Intent; import android.content.res.Configuration; +import android.graphics.Color; import android.os.Bundle; import android.support.design.widget.NavigationView; import android.support.v4.view.GravityCompat; @@ -252,9 +253,9 @@ private void repopulateAccountList(Account[] accounts) { // add all accounts to list for (int i = 0; i < accounts.length; i++) { try { - int[] rgb = calculateRGB(accounts[i]); - TextDrawable icon = new TextDrawable(accounts[i].name.substring(0, 1).toUpperCase(), - rgb[0], rgb[1], rgb[2]); + int[] rgb = calculateRGB(accounts[i].name); + TextDrawable icon = new TextDrawable(accounts[i].name.substring(0, 1).toUpperCase() + , rgb[0], rgb[1], rgb[2]); mNavigationView.getMenu().add(R.id.drawer_menu_accounts, Menu.NONE, 0, accounts[i].name).setIcon(icon); } catch (Exception e) { Log_OC.e(TAG, "Error calculating RGB value for account menu item.", e); @@ -269,10 +270,10 @@ private void repopulateAccountList(Account[] accounts) { } } - private int[] calculateRGB(Account account) throws UnsupportedEncodingException, NoSuchAlgorithmException { + private int[] calculateRGB(String accountName) throws UnsupportedEncodingException, NoSuchAlgorithmException { // using adapted algorithm from /core/js/placeholder.js:50 - int lastAtPos = account.name.lastIndexOf("@"); - String username = account.name.substring(0, lastAtPos); + int lastAtPos = accountName.lastIndexOf("@"); + String username = accountName.substring(0, lastAtPos); byte[] seed = username.getBytes("UTF-8"); MessageDigest md = MessageDigest.getInstance("MD5"); // Integer seedMd5Int = Math.abs(new String(Hex.encodeHex(seedMd5)) @@ -326,6 +327,18 @@ protected void setUsernameInDrawer(Account account) { int lastAtPos = account.name.lastIndexOf("@"); username.setText(account.name.substring(0, lastAtPos)); } + + ImageView usericon = (ImageView) ((NavigationView) findViewById(R.id.nav_view)) + .getHeaderView(0).findViewById(R.id.drawer_usericon); + try { + int[] rgb = calculateRGB(account.name); + TextDrawable icon = new TextDrawable( + account.name.substring(0, 1).toUpperCase(), rgb[0], rgb[1], rgb[2]); + usericon.setImageDrawable(icon); + } catch (Exception e) { + Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); + usericon.setImageResource(R.drawable.ic_account_circle); + } } } From d3e1a9be9013e5206d76c3b12d01ffd81c6a62fb Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 24 Mar 2016 02:41:59 +0100 Subject: [PATCH 09/84] account switching implemented (mostly by moving account related methods from FileActivity.javato DrawerActivity.java) --- .../android/ui/activity/DrawerActivity.java | 204 +++++++++++++- .../android/ui/activity/FileActivity.java | 264 ++---------------- 2 files changed, 219 insertions(+), 249 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index dad218799bc..b935d9ff1cd 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -2,6 +2,9 @@ import android.accounts.Account; import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.accounts.OperationCanceledException; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Color; @@ -20,9 +23,11 @@ import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; +import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.ui.TextDrawable; import com.owncloud.android.utils.BitmapUtils; @@ -47,11 +52,27 @@ public abstract class DrawerActivity extends ToolbarActivity { private NavigationView mNavigationView; private ImageView mAccountChooserToggle; - private List mAccountMenuItemIds = new ArrayList(); + /** OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located.*/ private Account mCurrentAccount; private boolean mIsAccountChooserActive; + /** Flag to signal that the activity will is finishing to enforce the creation of an ownCloud + * {@link Account} */ + private boolean mRedirectingToSetupAccount = false; + + /** Flag to signal when the value of mAccount was set */ + protected boolean mAccountWasSet; + + /** Flag to signal when the value of mAccount was restored from a saved state */ + protected boolean mAccountWasRestored; + + /** Capabilites of the server where {@link #mCurrentAccount} lives */ + private OCCapability mCapabilities; + + /** Access point to the cached database for the current ownCloud {@link Account} */ + private FileDataStorageManager mStorageManager = null; + /** * Initializes the drawer and its content. This method needs to be called after the content view has been set. */ @@ -167,8 +188,17 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { AccountManager am = AccountManager.get(getApplicationContext()); am.addAccount(MainApp.getAccountType(), null, null, null, DrawerActivity.this, null, null); + break; case R.id.drawer_menu_account_manage: Toast.makeText(getApplicationContext(), "Not implemented yet", Toast.LENGTH_SHORT); + break; + case Menu.NONE: + // account clicked + AccountUtils.setCurrentOwnCloudAccount( + getApplicationContext(),menuItem.getTitle().toString()); + restart(); + default: + Log_OC.i(TAG,"Unknown drawer menu item clicked: " + menuItem.getTitle()); } return true; @@ -402,4 +432,176 @@ public void onBackPressed() { private View findNavigationViewChildById(int id) { return ((NavigationView) findViewById(R.id.nav_view)).getHeaderView(0).findViewById(id); } + + public void restart(){ + Intent i = new Intent(this, FileDisplayActivity.class); + i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(i); + } + + /** + * Sets and validates the ownCloud {@link Account} associated to the Activity. + * + * If not valid, tries to swap it for other valid and existing ownCloud {@link Account}. + * + * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. + * + * @param account New {@link Account} to set. + * @param savedAccount When 'true', account was retrieved from a saved instance state. + */ + protected void setAccount(Account account, boolean savedAccount) { + Account oldAccount = mCurrentAccount; + boolean validAccount = + (account != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), + account.name)); + if (validAccount) { + mCurrentAccount = account; + mAccountWasSet = true; + mAccountWasRestored = (savedAccount || mCurrentAccount.equals(oldAccount)); + + } else { + swapToDefaultAccount(); + } + } + + + /** + * Tries to swap the current ownCloud {@link Account} for other valid and existing. + * + * If no valid ownCloud {@link Account} exists, the the user is requested + * to create a new ownCloud {@link Account}. + * + * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. + */ + protected void swapToDefaultAccount() { + // default to the most recently used account + Account newAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()); + if (newAccount == null) { + /// no account available: force account creation + createFirstAccount(); + mRedirectingToSetupAccount = true; + mAccountWasSet = false; + mAccountWasRestored = false; + + } else { + mAccountWasSet = true; + mAccountWasRestored = (newAccount.equals(mCurrentAccount)); + mCurrentAccount = newAccount; + } + } + + + /** + * Launches the account creation activity. To use when no ownCloud account is available + */ + private void createFirstAccount() { + AccountManager am = AccountManager.get(getApplicationContext()); + am.addAccount(MainApp.getAccountType(), + null, + null, + null, + this, + new AccountCreationCallback(), + null); + } + + /** + * Helper class handling a callback from the {@link AccountManager} after the creation of + * a new ownCloud {@link Account} finished, successfully or not. + * + * At this moment, only called after the creation of the first account. + */ + public class AccountCreationCallback implements AccountManagerCallback { + + @Override + public void run(AccountManagerFuture future) { + DrawerActivity.this.mRedirectingToSetupAccount = false; + boolean accountWasSet = false; + if (future != null) { + try { + Bundle result; + result = future.getResult(); + String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); + String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE); + if (AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name)) { + setAccount(new Account(name, type), false); + accountWasSet = true; + } + } catch (OperationCanceledException e) { + Log_OC.d(TAG, "Account creation canceled"); + + } catch (Exception e) { + Log_OC.e(TAG, "Account creation finished in exception: ", e); + } + + } else { + Log_OC.e(TAG, "Account creation callback with null bundle"); + } + if (!accountWasSet) { + moveTaskToBack(true); + } + } + } + + /** + * Called when the ownCloud {@link Account} associated to the Activity was just updated. + * + * Child classes must grant that state depending on the {@link Account} is updated. + */ + protected void onAccountSet(boolean stateWasRecovered) { + if (getAccount() != null) { + mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver()); + mCapabilities = mStorageManager.getCapability(mCurrentAccount.name); + + } else { + Log_OC.e(TAG, "onAccountChanged was called with NULL account associated!"); + } + } + + protected void setAccount(Account account) { + mCurrentAccount = account; + } + + + /** + * Getter for the capabilities of the server where the current OC account lives. + * + * @return Capabilities of the server where the current OC account lives. Null if the account is not + * set yet. + */ + public OCCapability getCapabilities() { + return mCapabilities; + } + + + /** + * Getter for the ownCloud {@link Account} where the main {@link OCFile} handled by the activity + * is located. + * + * @return OwnCloud {@link Account} where the main {@link OCFile} handled by the activity + * is located. + */ + public Account getAccount() { + return mCurrentAccount; + } + + @Override + protected void onStart() { + super.onStart(); + + if (mAccountWasSet) { + onAccountSet(mAccountWasRestored); + } + } + + /** + * @return 'True' when the Activity is finishing to enforce the setup of a new account. + */ + protected boolean isRedirectingToSetupAccount() { + return mRedirectingToSetupAccount; + } + + public FileDataStorageManager getStorageManager() { + return mStorageManager; + } } diff --git a/src/com/owncloud/android/ui/activity/FileActivity.java b/src/com/owncloud/android/ui/activity/FileActivity.java index e9870ff0a25..8734e9f50f8 100644 --- a/src/com/owncloud/android/ui/activity/FileActivity.java +++ b/src/com/owncloud/android/ui/activity/FileActivity.java @@ -23,40 +23,25 @@ import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.AccountManagerCallback; -import android.accounts.AccountManagerFuture; import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.content.res.Configuration; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; -import android.support.design.widget.NavigationView; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; -import android.support.v4.view.GravityCompat; -import android.support.v4.widget.DrawerLayout; -import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarDrawerToggle; -import android.support.v7.app.AppCompatActivity; -import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; -import android.widget.RelativeLayout; -import android.widget.TextView; import android.widget.Toast; -import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.authentication.AuthenticatorActivity; -import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.ui.helpers.FileOperationsHelper; import com.owncloud.android.files.services.FileDownloader; @@ -73,7 +58,6 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.operations.CreateShareWithShareeOperation; import com.owncloud.android.operations.GetSharesForFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation; @@ -83,15 +67,11 @@ import com.owncloud.android.operations.UpdateShareViaLinkOperation; import com.owncloud.android.services.OperationsService; import com.owncloud.android.services.OperationsService.OperationsServiceBinder; -import com.owncloud.android.ui.NavigationDrawerItem; -import com.owncloud.android.ui.adapter.NavigationDrawerListAdapter; import com.owncloud.android.ui.dialog.ConfirmationDialogFragment; import com.owncloud.android.ui.dialog.LoadingDialog; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog; import com.owncloud.android.utils.ErrorMessageAdapter; -import java.util.ArrayList; - /** * Activity with common behaviour for activities handling {@link OCFile}s in ownCloud @@ -112,9 +92,6 @@ public class FileActivity extends DrawerActivity private static final String KEY_WAITING_FOR_OP_ID = "WAITING_FOR_OP_ID"; private static final String KEY_ACTION_BAR_TITLE = "ACTION_BAR_TITLE"; - public static final int REQUEST_CODE__UPDATE_CREDENTIALS = 0; - public static final int REQUEST_CODE__LAST_SHARED = REQUEST_CODE__UPDATE_CREDENTIALS; - protected static final long DELAY_TO_REQUEST_OPERATIONS_LATER = 200; /* Dialog tags */ @@ -124,32 +101,15 @@ public class FileActivity extends DrawerActivity /** OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located.*/ private Account mAccount; - /** Capabilites of the server where {@link #mAccount} lives */ - private OCCapability mCapabilities; - /** Main {@link OCFile} handled by the activity.*/ private OCFile mFile; - - /** Flag to signal that the activity will is finishing to enforce the creation of an ownCloud - * {@link Account} */ - private boolean mRedirectingToSetupAccount = false; - - /** Flag to signal when the value of mAccount was set */ - protected boolean mAccountWasSet; - - /** Flag to signal when the value of mAccount was restored from a saved state */ - protected boolean mAccountWasRestored; - /** Flag to signal if the activity is launched by a notification */ private boolean mFromNotification; /** Messages handler associated to the main thread and the life cycle of the activity */ private Handler mHandler; - /** Access point to the cached database for the current ownCloud {@link Account} */ - private FileDataStorageManager mStorageManager = null; - private FileOperationsHelper mFileOperationsHelper; private ServiceConnection mOperationsServiceConnection = null; @@ -246,10 +206,6 @@ protected void onRestart() { @Override protected void onStart() { super.onStart(); - - if (mAccountWasSet) { - onAccountSet(mAccountWasRestored); - } } @Override @@ -270,6 +226,7 @@ protected void onPause() { super.onPause(); } + @Override protected void onDestroy() { if (mOperationsServiceConnection != null) { @@ -288,73 +245,6 @@ protected void onDestroy() { super.onDestroy(); } - /** - * Sets and validates the ownCloud {@link Account} associated to the Activity. - * - * If not valid, tries to swap it for other valid and existing ownCloud {@link Account}. - * - * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. - * - * @param account New {@link Account} to set. - * @param savedAccount When 'true', account was retrieved from a saved instance state. - */ - protected void setAccount(Account account, boolean savedAccount) { - Account oldAccount = mAccount; - boolean validAccount = - (account != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), - account.name)); - if (validAccount) { - mAccount = account; - mAccountWasSet = true; - mAccountWasRestored = (savedAccount || mAccount.equals(oldAccount)); - - } else { - swapToDefaultAccount(); - } - } - - - /** - * Tries to swap the current ownCloud {@link Account} for other valid and existing. - * - * If no valid ownCloud {@link Account} exists, the the user is requested - * to create a new ownCloud {@link Account}. - * - * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. - */ - private void swapToDefaultAccount() { - // default to the most recently used account - Account newAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()); - if (newAccount == null) { - /// no account available: force account creation - createFirstAccount(); - mRedirectingToSetupAccount = true; - mAccountWasSet = false; - mAccountWasRestored = false; - - } else { - mAccountWasSet = true; - mAccountWasRestored = (newAccount.equals(mAccount)); - mAccount = newAccount; - } - } - - - /** - * Launches the account creation activity. To use when no ownCloud account is available - */ - private void createFirstAccount() { - AccountManager am = AccountManager.get(getApplicationContext()); - am.addAccount(MainApp.getAccountType(), - null, - null, - null, - this, - new AccountCreationCallback(), - null); - } - - /** * {@inheritDoc} */ @@ -391,34 +281,6 @@ public void setFile(OCFile file) { mFile = file; } - - /** - * Getter for the ownCloud {@link Account} where the main {@link OCFile} handled by the activity - * is located. - * - * @return OwnCloud {@link Account} where the main {@link OCFile} handled by the activity - * is located. - */ - public Account getAccount() { - return mAccount; - } - - protected void setAccount(Account account) { - mAccount = account; - } - - - /** - * Getter for the capabilities of the server where the current OC account lives. - * - * @return Capabilities of the server where the current OC account lives. Null if the account is not - * set yet. - */ - public OCCapability getCapabilities() { - return mCapabilities; - } - - /** * @return Value of mFromNotification: True if the Activity is launched by a notification */ @@ -426,13 +288,6 @@ public boolean fromNotification() { return mFromNotification; } - /** - * @return 'True' when the Activity is finishing to enforce the setup of a new account. - */ - protected boolean isRedirectingToSetupAccount() { - return mRedirectingToSetupAccount; - } - public OperationsServiceBinder getOperationsServiceBinder() { return mOperationsServiceBinder; } @@ -441,67 +296,6 @@ protected ServiceConnection newTransferenceServiceConnection() { return null; } - /** - * Helper class handling a callback from the {@link AccountManager} after the creation of - * a new ownCloud {@link Account} finished, successfully or not. - * - * At this moment, only called after the creation of the first account. - */ - public class AccountCreationCallback implements AccountManagerCallback { - - @Override - public void run(AccountManagerFuture future) { - FileActivity.this.mRedirectingToSetupAccount = false; - boolean accountWasSet = false; - if (future != null) { - try { - Bundle result; - result = future.getResult(); - String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); - String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE); - if (AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name)) { - setAccount(new Account(name, type), false); - accountWasSet = true; - } - } catch (OperationCanceledException e) { - Log_OC.d(TAG, "Account creation canceled"); - - } catch (Exception e) { - Log_OC.e(TAG, "Account creation finished in exception: ", e); - } - - } else { - Log_OC.e(TAG, "Account creation callback with null bundle"); - } - if (!accountWasSet) { - moveTaskToBack(true); - } - } - - } - - - /** - * Called when the ownCloud {@link Account} associated to the Activity was just updated. - * - * Child classes must grant that state depending on the {@link Account} is updated. - */ - protected void onAccountSet(boolean stateWasRecovered) { - if (getAccount() != null) { - mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver()); - mCapabilities = mStorageManager.getCapability(mAccount.name); - - } else { - Log_OC.e(TAG, "onAccountChanged was called with NULL account associated!"); - } - } - - - public FileDataStorageManager getStorageManager() { - return mStorageManager; - } - - public OnRemoteOperationListener getRemoteOperationListener() { return this; } @@ -517,7 +311,7 @@ public FileOperationsHelper getFileOperationsHelper() { /** * - * @param operation Operation performed. + * @param operation Removal operation performed. * @param result Result of the removal. */ @Override @@ -531,12 +325,14 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe if (!result.isSuccess() && ( result.getCode() == ResultCode.UNAUTHORIZED || + result.isIdPRedirection() || (result.isException() && result.getException() instanceof AuthenticatorException) )) { requestCredentialsUpdate(this); if (result.getCode() == ResultCode.UNAUTHORIZED) { + dismissLoadingDialog(); Toast t = Toast.makeText(this, ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()), Toast.LENGTH_LONG); @@ -584,33 +380,16 @@ operation, getResources()), * Invalidates the credentials stored for the current OC account and requests new credentials to the user, * navigating to {@link AuthenticatorActivity} * - * Equivalent to call requestCredentialsUpdate(context, null); - * * @param context Android Context needed to access the {@link AccountManager}. Received as a parameter * to make the method accessible to {@link android.content.BroadcastReceiver}s. */ protected void requestCredentialsUpdate(Context context) { - requestCredentialsUpdate(context, null); - } - - /** - * Invalidates the credentials stored for the given OC account and requests new credentials to the user, - * navigating to {@link AuthenticatorActivity} - * - * @param context Android Context needed to access the {@link AccountManager}. Received as a parameter - * to make the method accessible to {@link android.content.BroadcastReceiver}s. - * @param account Stored OC account to request credentials update for. If null, current account will - * be used. - */ - protected void requestCredentialsUpdate(Context context, Account account) { try { /// step 1 - invalidate credentials of current account - if (account == null) { - account = getAccount(); - } OwnCloudClient client; - OwnCloudAccount ocAccount = new OwnCloudAccount(account, context); + OwnCloudAccount ocAccount = + new OwnCloudAccount(getAccount(), context); client = (OwnCloudClientManagerFactory.getDefaultSingleton(). removeClientFor(ocAccount)); if (client != null) { @@ -619,23 +398,23 @@ protected void requestCredentialsUpdate(Context context, Account account) { AccountManager am = AccountManager.get(context); if (cred.authTokenExpires()) { am.invalidateAuthToken( - account.type, + getAccount().type, cred.getAuthToken() ); } else { - am.clearPassword(account); + am.clearPassword(getAccount()); } } } /// step 2 - request credentials to user Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class); - updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account); + updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, getAccount()); updateAccountCredentials.putExtra( AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN); updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - startActivityForResult(updateAccountCredentials, REQUEST_CODE__UPDATE_CREDENTIALS); + startActivity(updateAccountCredentials); } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) { Toast.makeText(context, R.string.auth_account_does_not_exist, Toast.LENGTH_SHORT).show(); @@ -768,13 +547,10 @@ public FileUploaderBinder getFileUploaderBinder() { return mUploaderBinder; } - - public void restart(){ - Intent i = new Intent(this, FileDisplayActivity.class); - i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - startActivity(i); - } +// TODO re-enable when "Accounts" is available in Navigation Drawer +// public void closeDrawer() { +// mDrawerLayout.closeDrawers(); +// } @Override public void allFilesOption(){ @@ -837,24 +613,16 @@ public void onItemClick(AdapterView parent, View view, int position, long id) // TODO Enable when "On Device" is recovered ? // case 2: // MainApp.showOnlyFilesOnDevice(true); +// mDrawerLayout.closeDrawers(); // break; - case 1: // Uploads - Intent uploadListIntent = new Intent(getApplicationContext(), - UploadListActivity.class); - uploadListIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(uploadListIntent); - break; - - case 2: // Settings + case 1: // Settings Intent settingsIntent = new Intent(getApplicationContext(), Preferences.class); startActivity(settingsIntent); //mDrawerLayout.closeDrawers(); break; } - mDrawerLayout.closeDrawers(); } } - } From 4810094f31a12e3d6cfcf3173644e309c4fe2d64 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 24 Mar 2016 13:16:52 +0100 Subject: [PATCH 10/84] preserve drawer state on screen rotation and resumes, move color calculation code to utils class --- .../android/ui/activity/DrawerActivity.java | 246 +++++++++--------- .../owncloud/android/utils/BitmapUtils.java | 30 ++- 2 files changed, 147 insertions(+), 129 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index b935d9ff1cd..99e890c590c 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -7,7 +7,6 @@ import android.accounts.OperationCanceledException; import android.content.Intent; import android.content.res.Configuration; -import android.graphics.Color; import android.os.Bundle; import android.support.design.widget.NavigationView; import android.support.v4.view.GravityCompat; @@ -31,50 +30,72 @@ import com.owncloud.android.ui.TextDrawable; import com.owncloud.android.utils.BitmapUtils; -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - /** * Base class to handle setup of the drawer implementation. */ public abstract class DrawerActivity extends ToolbarActivity { private static final String TAG = DrawerActivity.class.getSimpleName(); + private static final String KEY_IS_ACCOUNT_CHOOSER_ACTIVE = "IS_ACCOUNT_CHOOSER_ACTIVE"; - // Navigation Drawer + /** + * Reference to the drawer layout. + */ private DrawerLayout mDrawerLayout; + + /** + * Reference to the drawer toggle. + */ private ActionBarDrawerToggle mDrawerToggle; + + /** + * Reference to the navigation view. + */ private NavigationView mNavigationView; + + /** + * Reference to the account chooser toogle. + */ private ImageView mAccountChooserToggle; - /** OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located.*/ + /** + * ownCloud {@link Account} where the main {@link OCFile} handled by the activity is located. + */ private Account mCurrentAccount; + /** + * Flag to signal if the account chooser is active. + */ private boolean mIsAccountChooserActive; - /** Flag to signal that the activity will is finishing to enforce the creation of an ownCloud - * {@link Account} */ + /** + * Flag to signal that the activity will is finishing to enforce the creation of an ownCloud {@link Account}. + */ private boolean mRedirectingToSetupAccount = false; - /** Flag to signal when the value of mAccount was set */ + /** + * Flag to signal when the value of mAccount was set. + */ protected boolean mAccountWasSet; - /** Flag to signal when the value of mAccount was restored from a saved state */ + /** + * Flag to signal when the value of mAccount was restored from a saved state. + */ protected boolean mAccountWasRestored; - /** Capabilites of the server where {@link #mCurrentAccount} lives */ + /** + * Capabilites of the server where {@link #mCurrentAccount} lives. + */ private OCCapability mCapabilities; - /** Access point to the cached database for the current ownCloud {@link Account} */ + /** + * Access point to the cached database for the current ownCloud {@link Account}. + */ private FileDataStorageManager mStorageManager = null; /** - * Initializes the drawer and its content. This method needs to be called after the content view has been set. + * Initializes the drawer and its content. + * This method needs to be called after the content view has been set. */ protected void setupDrawer() { mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); @@ -95,52 +116,15 @@ public void onClick(View v) { }); } - // TODO re-enable when "Accounts" is available in Navigation Drawer -// // load Account in the Drawer Title -// // User-Icon -// ImageView userIcon = (ImageView) navigationDrawerLayout.findViewById(R.id.drawer_userIcon); -// userIcon.setImageResource(DisplayUtils.getSeasonalIconId()); -// -// // Username -// TextView username = (TextView) navigationDrawerLayout.findViewById(R.id.drawer_username); -// Account account = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()); -// -// if (account != null) { -// int lastAtPos = account.name.lastIndexOf("@"); -// username.setText(account.name.substring(0, lastAtPos)); -// } -/* - // Display username in drawer - setUsernameInDrawer(navigationDrawerLayout, AccountUtils.getCurrentOwnCloudAccount(getApplicationContext())); - - // load slide menu items - mDrawerTitles = getResources().getStringArray(R.array.drawer_items); - - // nav drawer content description from resources - mDrawerContentDescriptions = getResources(). - getStringArray(R.array.drawer_content_descriptions); - - // nav drawer items - mDrawerItems = new ArrayList(); - // adding nav drawer items to array - // TODO re-enable when "Accounts" is available in Navigation Drawer - // Accounts - // mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[0], - // mDrawerContentDescriptions[0])); - // All Files - mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[0], mDrawerContentDescriptions[0], - R.drawable.ic_folder_open)); - - // TODO Enable when "On Device" is recovered - // On Device - //mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[2], - // mDrawerContentDescriptions[2])); -*/ mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) { /** Called when a drawer has settled in a completely closed state. */ public void onDrawerClosed(View view) { super.onDrawerClosed(view); + // standard behavior of drawer is to switch to the standard menu on closing + if (mIsAccountChooserActive) { + toggleAccountList(); + } updateActionBarTitleAndHomeButton(null); invalidateOptionsMenu(); } @@ -153,10 +137,7 @@ public void onDrawerOpened(View drawerView) { invalidateOptionsMenu(); } }; - /* - // Set the list's click listener - mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); -*/ + // Set the drawer toggle as the DrawerListener mDrawerLayout.setDrawerListener(mDrawerToggle); mDrawerToggle.setDrawerIndicatorEnabled(false); @@ -195,18 +176,22 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { case Menu.NONE: // account clicked AccountUtils.setCurrentOwnCloudAccount( - getApplicationContext(),menuItem.getTitle().toString()); + getApplicationContext(), menuItem.getTitle().toString()); restart(); default: - Log_OC.i(TAG,"Unknown drawer menu item clicked: " + menuItem.getTitle()); + Log_OC.i(TAG, "Unknown drawer menu item clicked: " + menuItem.getTitle()); } return true; } }); - // hide accounts - mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false); + // handle correct state + if (mIsAccountChooserActive) { + mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, true); + } else { + mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false); + } } /** @@ -283,7 +268,7 @@ private void repopulateAccountList(Account[] accounts) { // add all accounts to list for (int i = 0; i < accounts.length; i++) { try { - int[] rgb = calculateRGB(accounts[i].name); + int[] rgb = BitmapUtils.calculateRGB(accounts[i].name); TextDrawable icon = new TextDrawable(accounts[i].name.substring(0, 1).toUpperCase() , rgb[0], rgb[1], rgb[2]); mNavigationView.getMenu().add(R.id.drawer_menu_accounts, Menu.NONE, 0, accounts[i].name).setIcon(icon); @@ -295,26 +280,7 @@ private void repopulateAccountList(Account[] accounts) { } // adding sets menu group back to visible, so safety check and setting invisible - if (!mIsAccountChooserActive) { - mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false); - } - } - - private int[] calculateRGB(String accountName) throws UnsupportedEncodingException, NoSuchAlgorithmException { - // using adapted algorithm from /core/js/placeholder.js:50 - int lastAtPos = accountName.lastIndexOf("@"); - String username = accountName.substring(0, lastAtPos); - byte[] seed = username.getBytes("UTF-8"); - MessageDigest md = MessageDigest.getInstance("MD5"); -// Integer seedMd5Int = Math.abs(new String(Hex.encodeHex(seedMd5)) -// .hashCode()); - Integer seedMd5Int = String.format(Locale.ROOT, "%032x", - new BigInteger(1, md.digest(seed))).hashCode(); - - double maxRange = Integer.MAX_VALUE; - float hue = (float) (seedMd5Int / maxRange * 360); - - return BitmapUtils.HSLtoRGB(hue, 90.0f, 65.0f, 1.0f); + showMenu(); } /** @@ -358,38 +324,67 @@ protected void setUsernameInDrawer(Account account) { username.setText(account.name.substring(0, lastAtPos)); } - ImageView usericon = (ImageView) ((NavigationView) findViewById(R.id.nav_view)) + ImageView userIcon = (ImageView) ((NavigationView) findViewById(R.id.nav_view)) .getHeaderView(0).findViewById(R.id.drawer_usericon); try { - int[] rgb = calculateRGB(account.name); + int[] rgb = BitmapUtils.calculateRGB(account.name); TextDrawable icon = new TextDrawable( account.name.substring(0, 1).toUpperCase(), rgb[0], rgb[1], rgb[2]); - usericon.setImageDrawable(icon); + userIcon.setImageDrawable(icon); } catch (Exception e) { Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); - usericon.setImageResource(R.drawable.ic_account_circle); + userIcon.setImageResource(R.drawable.ic_account_circle); } } } /** - * Toggle between drawer menu and account list. + * Toggle between standard menu and account list including saving the state. */ private void toggleAccountList() { + mIsAccountChooserActive = !mIsAccountChooserActive; + showMenu(); + } + + /** + * depending on the #mIsAccountChooserActive flag shows the account chooser or the standard menu. + */ + private void showMenu() { if (mIsAccountChooserActive) { - // TODO close accounts list and display drawer menu again + mAccountChooserToggle.setImageResource(R.drawable.ic_up); + mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, true); + mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, false); + } else { mAccountChooserToggle.setImageResource(R.drawable.ic_down); mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false); mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, true); + } + } - } else { - // TODO show accounts list - mAccountChooserToggle.setImageResource(R.drawable.ic_up); - mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, true); - mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, false); + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (savedInstanceState != null) { + mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false); } + } - mIsAccountChooserActive = !mIsAccountChooserActive; + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + outState.putBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, mIsAccountChooserActive); + } + + @Override + public void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + + mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false); + + // (re-)setup drawer state + showMenu(); } @Override @@ -433,21 +428,21 @@ private View findNavigationViewChildById(int id) { return ((NavigationView) findViewById(R.id.nav_view)).getHeaderView(0).findViewById(id); } - public void restart(){ + public void restart() { Intent i = new Intent(this, FileDisplayActivity.class); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(i); } /** - * Sets and validates the ownCloud {@link Account} associated to the Activity. - * - * If not valid, tries to swap it for other valid and existing ownCloud {@link Account}. - * - * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. + * Sets and validates the ownCloud {@link Account} associated to the Activity. + *

+ * If not valid, tries to swap it for other valid and existing ownCloud {@link Account}. + *

+ * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. * - * @param account New {@link Account} to set. - * @param savedAccount When 'true', account was retrieved from a saved instance state. + * @param account New {@link Account} to set. + * @param savedAccount When 'true', account was retrieved from a saved instance state. */ protected void setAccount(Account account, boolean savedAccount) { Account oldAccount = mCurrentAccount; @@ -464,14 +459,13 @@ protected void setAccount(Account account, boolean savedAccount) { } } - /** - * Tries to swap the current ownCloud {@link Account} for other valid and existing. - * - * If no valid ownCloud {@link Account} exists, the the user is requested - * to create a new ownCloud {@link Account}. - * - * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. + * Tries to swap the current ownCloud {@link Account} for other valid and existing. + *

+ * If no valid ownCloud {@link Account} exists, the the user is requested + * to create a new ownCloud {@link Account}. + *

+ * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. */ protected void swapToDefaultAccount() { // default to the most recently used account @@ -490,9 +484,8 @@ protected void swapToDefaultAccount() { } } - /** - * Launches the account creation activity. To use when no ownCloud account is available + * Launches the account creation activity. To use when no ownCloud account is available. */ private void createFirstAccount() { AccountManager am = AccountManager.get(getApplicationContext()); @@ -508,7 +501,7 @@ private void createFirstAccount() { /** * Helper class handling a callback from the {@link AccountManager} after the creation of * a new ownCloud {@link Account} finished, successfully or not. - * + *

* At this moment, only called after the creation of the first account. */ public class AccountCreationCallback implements AccountManagerCallback { @@ -544,15 +537,14 @@ public void run(AccountManagerFuture future) { } /** - * Called when the ownCloud {@link Account} associated to the Activity was just updated. - * - * Child classes must grant that state depending on the {@link Account} is updated. + * Called when the ownCloud {@link Account} associated to the Activity was just updated. + *

+ * Child classes must grant that state depending on the {@link Account} is updated. */ protected void onAccountSet(boolean stateWasRecovered) { if (getAccount() != null) { mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver()); mCapabilities = mStorageManager.getCapability(mCurrentAccount.name); - } else { Log_OC.e(TAG, "onAccountChanged was called with NULL account associated!"); } @@ -562,24 +554,22 @@ protected void setAccount(Account account) { mCurrentAccount = account; } - /** * Getter for the capabilities of the server where the current OC account lives. * - * @return Capabilities of the server where the current OC account lives. Null if the account is not - * set yet. + * @return Capabilities of the server where the current OC account lives. Null if the account is not + * set yet. */ public OCCapability getCapabilities() { return mCapabilities; } - /** * Getter for the ownCloud {@link Account} where the main {@link OCFile} handled by the activity * is located. * - * @return OwnCloud {@link Account} where the main {@link OCFile} handled by the activity - * is located. + * @return OwnCloud {@link Account} where the main {@link OCFile} handled by the activity + * is located. */ public Account getAccount() { return mCurrentAccount; diff --git a/src/com/owncloud/android/utils/BitmapUtils.java b/src/com/owncloud/android/utils/BitmapUtils.java index f156ff8be4b..84d7b5a992c 100644 --- a/src/com/owncloud/android/utils/BitmapUtils.java +++ b/src/com/owncloud/android/utils/BitmapUtils.java @@ -30,6 +30,11 @@ import android.webkit.MimeTypeMap; import java.io.File; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Locale; /** * Utility class with methods for decoding Bitmaps. @@ -266,5 +271,28 @@ public static boolean isImage(File file) { return (mimeType != null && mimeType.startsWith("image/")); } - + + /** + * calculates the RGB value based on a given account name. + * + * @param accountName The account name + * @return corresponding RGB color + * @throws UnsupportedEncodingException if the charset is not supported + * @throws NoSuchAlgorithmException if the specified algorithm is not available + */ + public static int[] calculateRGB(String accountName) throws UnsupportedEncodingException, NoSuchAlgorithmException { + // using adapted algorithm from /core/js/placeholder.js:50 + int lastAtPos = accountName.lastIndexOf("@"); + String username = accountName.substring(0, lastAtPos); + byte[] seed = username.getBytes("UTF-8"); + MessageDigest md = MessageDigest.getInstance("MD5"); +// Integer seedMd5Int = Math.abs(new String(Hex.encodeHex(seedMd5)).hashCode()); + Integer seedMd5Int = String.format(Locale.ROOT, "%032x", + new BigInteger(1, md.digest(seed))).hashCode(); + + double maxRange = Integer.MAX_VALUE; + float hue = (float) (seedMd5Int / maxRange * 360); + + return BitmapUtils.HSLtoRGB(hue, 90.0f, 65.0f, 1.0f); + } } From 13487e990a4f73ae761403a37ee8327a8136bcc8 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 24 Mar 2016 13:45:25 +0100 Subject: [PATCH 11/84] housekeeping removing old account switching code --- res/layout/drawer.xml | 27 --- res/layout/drawer_account_group.xml | 25 --- res/layout/drawer_list_item.xml | 54 ----- res/layout/drawer_radiobutton.xml | 29 --- .../android/ui/activity/DrawerActivity.java | 19 ++ .../android/ui/activity/FileActivity.java | 50 +---- .../android/ui/activity/ToolbarActivity.java | 27 ++- .../adapter/NavigationDrawerListAdapter.java | 210 ------------------ 8 files changed, 44 insertions(+), 397 deletions(-) delete mode 100644 res/layout/drawer_account_group.xml delete mode 100644 res/layout/drawer_list_item.xml delete mode 100644 res/layout/drawer_radiobutton.xml delete mode 100644 src/com/owncloud/android/ui/adapter/NavigationDrawerListAdapter.java diff --git a/res/layout/drawer.xml b/res/layout/drawer.xml index 4bccbd79533..9360511c514 100644 --- a/res/layout/drawer.xml +++ b/res/layout/drawer.xml @@ -29,31 +29,4 @@ app:headerLayout="@layout/drawer_header" app:menu="@menu/drawer_menu"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/layout/drawer_account_group.xml b/res/layout/drawer_account_group.xml deleted file mode 100644 index 01fcddd10dc..00000000000 --- a/res/layout/drawer_account_group.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - \ No newline at end of file diff --git a/res/layout/drawer_list_item.xml b/res/layout/drawer_list_item.xml deleted file mode 100644 index 481a33b70f8..00000000000 --- a/res/layout/drawer_list_item.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - android:filterTouchesWhenObscured="true" - > - - - - - - diff --git a/res/layout/drawer_radiobutton.xml b/res/layout/drawer_radiobutton.xml deleted file mode 100644 index 22d6ceb79b5..00000000000 --- a/res/layout/drawer_radiobutton.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - \ No newline at end of file diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index 99e890c590c..30cab8be2d7 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -1,3 +1,22 @@ +/** + * ownCloud Android client application + * + * @author Andy Scherzinger + * Copyright (C) 2016 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package com.owncloud.android.ui.activity; import android.accounts.Account; diff --git a/src/com/owncloud/android/ui/activity/FileActivity.java b/src/com/owncloud/android/ui/activity/FileActivity.java index 8734e9f50f8..33d0d301c92 100644 --- a/src/com/owncloud/android/ui/activity/FileActivity.java +++ b/src/com/owncloud/android/ui/activity/FileActivity.java @@ -74,8 +74,7 @@ /** - * Activity with common behaviour for activities handling {@link OCFile}s in ownCloud - * {@link Account}s . + * Activity with common behaviour for activities handling {@link OCFile}s in ownCloud {@link Account}s . */ public class FileActivity extends DrawerActivity implements OnRemoteOperationListener, ComponentsGetter, SslUntrustedCertDialog.OnSslUntrustedCertListener { @@ -122,9 +121,6 @@ public class FileActivity extends DrawerActivity protected FileUploaderBinder mUploaderBinder = null; private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null; - // TODO re-enable when "Accounts" is available in Navigation Drawer -// protected boolean mShowAccounts = false; - /** * Loads the ownCloud {@link Account} and main {@link OCFile} to be handled by the instance of * the {@link FileActivity}. @@ -524,7 +520,6 @@ public void onServiceConnected(ComponentName component, IBinder service) { } } - @Override public void onServiceDisconnected(ComponentName component) { if (component.equals(new ComponentName(FileActivity.this, OperationsService.class))) { @@ -535,23 +530,16 @@ public void onServiceDisconnected(ComponentName component) { } } - @Override public FileDownloaderBinder getFileDownloaderBinder() { return mDownloaderBinder; } - @Override public FileUploaderBinder getFileUploaderBinder() { return mUploaderBinder; } -// TODO re-enable when "Accounts" is available in Navigation Drawer -// public void closeDrawer() { -// mDrawerLayout.closeDrawers(); -// } - @Override public void allFilesOption(){ restart(); @@ -590,39 +578,5 @@ public void onCancelCertificate() { // nothing to do } - private class DrawerItemClickListener implements ListView.OnItemClickListener { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - // TODO re-enable when "Accounts" is available in Navigation Drawer -// if (mShowAccounts && position > 0){ -// position = position - 1; -// } - switch (position){ - // TODO re-enable when "Accounts" is available in Navigation Drawer -// case 0: // Accounts -// mShowAccounts = !mShowAccounts; -// mNavigationDrawerAdapter.setShowAccounts(mShowAccounts); -// mNavigationDrawerAdapter.notifyDataSetChanged(); -// break; - - case 0: // All Files - allFilesOption(); - //mDrawerLayout.closeDrawers(); - break; - - // TODO Enable when "On Device" is recovered ? -// case 2: -// MainApp.showOnlyFilesOnDevice(true); -// mDrawerLayout.closeDrawers(); -// break; - - case 1: // Settings - Intent settingsIntent = new Intent(getApplicationContext(), - Preferences.class); - startActivity(settingsIntent); - //mDrawerLayout.closeDrawers(); - break; - } - } - } +} } diff --git a/src/com/owncloud/android/ui/activity/ToolbarActivity.java b/src/com/owncloud/android/ui/activity/ToolbarActivity.java index c24b7b18e38..b35bf556d31 100644 --- a/src/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/src/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -1,3 +1,22 @@ +/** + * ownCloud Android client application + * + * @author Andy Scherzinger + * Copyright (C) 2016 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package com.owncloud.android.ui.activity; import android.os.Bundle; @@ -43,7 +62,7 @@ protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { String title = getString(R.string.default_display_name_for_root_folder); // default boolean inRoot; - // choose the appropiate title + // choose the appropriate title inRoot = ( chosenFile == null || (chosenFile.isFolder() && chosenFile.getParentId() == FileDataStorageManager.ROOT_PARENT_ID) @@ -52,11 +71,11 @@ protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { title = chosenFile.getFileName(); } - /// set the chosen title + // set the chosen title ActionBar actionBar = getSupportActionBar(); actionBar.setTitle(title); - /// also as content description + // also as content description View actionBarTitleView = getWindow().getDecorView().findViewById( getResources().getIdentifier("action_bar_title", "id", "android") ); @@ -65,7 +84,7 @@ protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { actionBarTitleView.setContentDescription(title); } - /// set home button properties + // set home button properties actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayShowTitleEnabled(true); } diff --git a/src/com/owncloud/android/ui/adapter/NavigationDrawerListAdapter.java b/src/com/owncloud/android/ui/adapter/NavigationDrawerListAdapter.java deleted file mode 100644 index 40ecb1c8de0..00000000000 --- a/src/com/owncloud/android/ui/adapter/NavigationDrawerListAdapter.java +++ /dev/null @@ -1,210 +0,0 @@ -/** - * ownCloud Android client application - * - * @author tobiasKaminsky - * @author masensio - * Copyright (C) 2016 ownCloud GmbH. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -package com.owncloud.android.ui.adapter; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.owncloud.android.MainApp; -import com.owncloud.android.R; -import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.ui.NavigationDrawerItem; -import com.owncloud.android.ui.activity.FileActivity; - -import java.util.ArrayList; - -// TODO remove complete class as soon as account switcher v2 is implemented, keep it until then for inspiration -public class NavigationDrawerListAdapter extends BaseAdapter { - - private final static String TAG = NavigationDrawerListAdapter.class.getSimpleName(); - - private Context mContext; - - private ArrayList mNavigationDrawerItems; - private ArrayList mAll = new ArrayList(); - private Account[] mAccounts; - private boolean mShowAccounts; - private Account mCurrentAccount; - private FileActivity mFileActivity; - - - public NavigationDrawerListAdapter(Context context, FileActivity fileActivity, - ArrayList navigationDrawerItems){ - mFileActivity = fileActivity; - mContext = context; - mNavigationDrawerItems = navigationDrawerItems; - - updateAccountList(); - - mAll.addAll(mNavigationDrawerItems); - } - - public void updateAccountList(){ - AccountManager am = (AccountManager) mContext.getSystemService(mContext.ACCOUNT_SERVICE); - mAccounts = am.getAccountsByType(MainApp.getAccountType()); - mCurrentAccount = AccountUtils.getCurrentOwnCloudAccount(mContext); - } - - @Override - public int getCount() { - if (mShowAccounts){ - return mNavigationDrawerItems.size() + 1; - } else { - return mNavigationDrawerItems.size(); - } - } - - @Override - public Object getItem(int position) { - //return all.get(position); - return null; - } - - @Override - public long getItemId(int position) { - return 0; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - - LayoutInflater inflator = (LayoutInflater) mContext - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - if (mAll.size() > position) { - // Normal entry - if (mAll.get(position) instanceof NavigationDrawerItem){ - NavigationDrawerItem navItem = (NavigationDrawerItem) mAll.get(position); - - View view = inflator.inflate(R.layout.drawer_list_item, null); - - LinearLayout itemLayout = (LinearLayout) view.findViewById(R.id.itemLayout); - itemLayout.setContentDescription(navItem.getContentDescription()); - - TextView itemText = (TextView) view.findViewById(R.id.itemTitle); - itemText.setText(navItem.getTitle()); - - if(navItem.getIcon()!=0) { - ImageView itemImage = (ImageView) view.findViewById(R.id.itemIcon); - itemImage.setImageResource(navItem.getIcon()); - } - - return view; - } - // TODO re-enable when "Accounts" is available in Navigation Drawer - // Account -// if (mAll.get(position) instanceof Account[]){ -// final View view = inflator.inflate(R.layout.drawer_account_group, null); -// -// final RadioGroup group = (RadioGroup) view.findViewById(R.id.drawer_radio_group); -// -// for (Account account : mAccounts) { -// RadioButton rb = new RadioButton(mContext); -// -// rb.setText(account.name); -// rb.setContentDescription(account.name); -// rb.setTextColor(Color.BLACK); -// rb.setEllipsize(TextUtils.TruncateAt.MIDDLE); -// rb.setSingleLine(); -// rb.setCompoundDrawablePadding(30); -// -// -// try { -// // using adapted algorithm from /core/js/placeholder.js:50 -// int lastAtPos = account.name.lastIndexOf("@"); -// String username = account.name.substring(0, lastAtPos); -// byte[] seed = username.getBytes("UTF-8"); -// MessageDigest md = MessageDigest.getInstance("MD5"); -//// Integer seedMd5Int = Math.abs(new String(Hex.encodeHex(seedMd5)) -//// .hashCode()); -// Integer seedMd5Int = String.format(Locale.ROOT, "%032x", -// new BigInteger(1, md.digest(seed))).hashCode(); -// -// double maxRange = java.lang.Integer.MAX_VALUE; -// float hue = (float) (seedMd5Int / maxRange * 360); -// -// int[] rgb = BitmapUtils.HSLtoRGB(hue, 90.0f, 65.0f, 1.0f); -// -// TextDrawable text = new TextDrawable(username.substring(0, 1).toUpperCase(), -// rgb[0], rgb[1], rgb[2]); -// rb.setCompoundDrawablesWithIntrinsicBounds(text, null, null, null); -// -// -// } catch (Exception e){ -// Log_OC.d(TAG, e.toString()); -// rb.setTextColor(mContext.getResources().getColor(R.color.black)); -// } -// RadioGroup.LayoutParams params = new RadioGroup.LayoutParams( -// LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); -// params.weight=1.0f; -// params.setMargins(15, 5, 5, 5); -// -// // Check the current account that is being used -// if (account.name.equals(mCurrentAccount.name)) { -// rb.setChecked(true); -// } else { -// rb.setChecked(false); -// } -// -// group.addView(rb, params); -// } -// -// group.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener(){ -// public void onCheckedChanged(RadioGroup group, int checkedId) { -// // checkedId is the RadioButton selected -// RadioButton rb = (RadioButton) view.findViewById(checkedId); -// -// AccountUtils.setCurrentOwnCloudAccount(mContext,rb.getText().toString()); -// notifyDataSetChanged(); -// mFileActivity.closeDrawer(); -// -// // restart the main activity -// mFileActivity.restart(); -// } -// }); -// -// return view; -// } - } - return convertView; - } - - //TODO re-enable when "Accounts" is available in Navigation Drawer - // TODO update Account List after creating a new account and on fresh installation -// public void setShowAccounts(boolean value){ -// mAll.clear(); -// mAll.addAll(mNavigationDrawerItems); -// -// if (value){ -// mAll.add(1, mAccounts); -// } -// mShowAccounts = value; -// } -} From d7dfb2789f85362355ee976a914df4e23afd8c3f Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 24 Mar 2016 14:27:29 +0100 Subject: [PATCH 12/84] split toolbar content method --- .../android/ui/activity/ToolbarActivity.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/ToolbarActivity.java b/src/com/owncloud/android/ui/activity/ToolbarActivity.java index b35bf556d31..a41cfeabefa 100644 --- a/src/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/src/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -71,9 +71,22 @@ protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { title = chosenFile.getFileName(); } + updateActionBarTitleAndHomeButtonByString(title); + } + + /** + * Updates title bar and home buttons (state and icon). + */ + protected void updateActionBarTitleAndHomeButtonByString(String title) { + String titleToSet = getString(R.string.app_name); // default + + if (title != null) { + titleToSet = title; + } + // set the chosen title ActionBar actionBar = getSupportActionBar(); - actionBar.setTitle(title); + actionBar.setTitle(titleToSet); // also as content description View actionBarTitleView = getWindow().getDecorView().findViewById( @@ -81,7 +94,7 @@ protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { ); // TODO remove legacy code if (actionBarTitleView != null) { // it's null in Android 2.x - actionBarTitleView.setContentDescription(title); + actionBarTitleView.setContentDescription(titleToSet); } // set home button properties From 6711ff70595842a8a6514d32cd6f77a543150e20 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 24 Mar 2016 22:17:04 +0100 Subject: [PATCH 13/84] wip account management - account list changes aren't propagated back to the drawer yet, preferences haven't been cleaned up yet --- AndroidManifest.xml | 1 + res/drawable-hdpi/ic_key.png | Bin 0 -> 595 bytes res/drawable-mdpi/ic_key.png | Bin 0 -> 447 bytes res/drawable-xhdpi/ic_key.png | Bin 0 -> 719 bytes res/drawable-xxhdpi/ic_key.png | Bin 0 -> 1055 bytes res/layout/account_item.xml | 68 +++++ res/layout/accounts_layout.xml | 37 +++ .../android/ui/activity/DrawerActivity.java | 11 +- .../android/ui/activity/FileActivity.java | 1 - .../ui/activity/ManageAccountsActivity.java | 269 ++++++++++++++++++ .../android/ui/activity/Preferences.java | 71 +++-- .../ui/adapter/AccountListAdapter.java | 161 +++++++++++ .../android/ui/adapter/AccountListItem.java | 39 +++ 13 files changed, 627 insertions(+), 31 deletions(-) create mode 100644 res/drawable-hdpi/ic_key.png create mode 100644 res/drawable-mdpi/ic_key.png create mode 100644 res/drawable-xhdpi/ic_key.png create mode 100644 res/drawable-xxhdpi/ic_key.png create mode 100644 res/layout/account_item.xml create mode 100644 res/layout/accounts_layout.xml create mode 100644 src/com/owncloud/android/ui/activity/ManageAccountsActivity.java create mode 100644 src/com/owncloud/android/ui/adapter/AccountListAdapter.java create mode 100644 src/com/owncloud/android/ui/adapter/AccountListItem.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index be718438c94..69a6990892a 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -72,6 +72,7 @@ + |k1|%Oc%$NbB*pj^6U4S$Y{B+)352QE?JR*yM zvQ&rUPlPeufHm>3#+V#(SPFjv*0;-%c~^7IqYA*l#{lWTlJ1!wnHi zNtv0dheQvFUUy^V{(q=7WwNaWi>t>C%bnU=wh5ox(tcyb@}}0ZrfZQby6bkhc}$+0 zK2f6RxZ8=;#bUop^W}bjn{D2Iv$Y8Xl25F0S$xqVTx;sPRj;fHBrW)57~>nouiaWc z*YEjzk zblFjWWq0u@hC5tM3FhDVwnnWD4Xv#`Wu>KL{>Y+kYIOUa@BCsnXROM5dTn9V-nxTP zzkA;({?F)H3AFxv^T7vQ-|N_S%sPJB|3c}x+$e_Zw_kN61%D>?M7}QD?f!Tvf1t?P z%J=(MygDVc#LfL%`=j5%MvHTM9{o){;nVU@ou{WS&8^P1YtiP*+>a}2Bb~$&EM)d= zzNV%A%p)}Xl1c5&yzT1FbC(}6TtKSPDj(q#+`j9#8Djv*e$-%j4>#q221wtr`=$rHJQT3b8B z7{Bs5tqHynvBV&=Nu}U#X`@U`mx!CR<3Y8xUHJCf-az1uov%6_H7p8oKn#*PcfVKNUFgyq8Y$(wT5Fh0ismxbIJm-gIk^J4(k3 z-!;sQ_@l;iSgRpu6}QM_k-vvN+iX;-uJsCC^62-!6W{+%>kkS_skgV4 hPyY94%9N)cIE{K@KhM^Xe*z3*22WQ%mvv4FO#l>!xJUp1 literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_key.png b/res/drawable-xhdpi/ic_key.png new file mode 100644 index 0000000000000000000000000000000000000000..8b44a8181600bc657eef2891f61d1a11c31c9875 GIT binary patch literal 719 zcmV;=0x_wEin9*@W4 z@pyJGGRPonZ5qe{XI1qmkZGR#T}0jiubgvpl}hF7jtVd{Gcy%M(JkN-5cbh>z`S$L zRIAl*6Axgm&8g}`VE-01S48A;9LG<`5g_PRG{#&5?gObIH1`4Lv)Sy=TCMha>;bH` zxu*F&+t3vena*ajA8WPR`y>N&EFo*{VO9MA92k-QvUBcalFvlPT~^iE5t;`$7)8ECU(%5Ue~k)&p!Wi|+c+yLemf$Jjj96(i119w>47dB&W6h%wM zn2{PU7K_pm065dw0GhSJ zB+PdNP}Qkk1MBtri(!t9Ex`Xu`x5HeB~q!>^f1T97C==$^%+~v=ktfw7n`;6qyn^e zp=b2&MCmXL-xy=Mwb6-mppF2mtE&q^5LD^C7i55WRo%)p2HMDsF+D~9_5JmMuLzZX{)XLx4Drmqg?u@Mi?ts;XC${yN*R55#f2z}n8b zoe-+3g;J^XI7#ze0{|3@#k(SM4)`^M=8A}1D3wapq|I*{0M=&0Id>em2Q&uIa=;_! z+=+304?~~V=jQsoVy&HJhyGUacsw4D$K%<}`~@Ji+dwKlgrWcd002ovPDHLkV1jXN BLa+b; literal 0 HcmV?d00001 diff --git a/res/drawable-xxhdpi/ic_key.png b/res/drawable-xxhdpi/ic_key.png new file mode 100644 index 0000000000000000000000000000000000000000..27f889e984cd2743fde60195a50ffb002eb50190 GIT binary patch literal 1055 zcmV+)1mOFLP)dg~IpK)6=2p0cRdiu~;ljOiX+T;2eOrQ~U-4xJ^WtmzI{UHyVwW>A{aYAltT& z64B=Xb`SZ#KLhy0_x(Gj1K3Dk0lBWbb!BDc0)R6k{BH(ux!G)%>h=0V(?RsIVL($; zQ!htRGz;L(jB1#RqUcnuR{O_v7;9NKpg5c#0PM=Rjzy)^Auav-zGOO(q@Dp;mUSM$Tbo$RyQ{0KzUe@c);z`HyVbV_>d{1WpjxfY z8_wmat|O>eEEeKhxsiIb)>km(1a_naB|nHKfy2&o21Z2t%H{HVMpJ3i6%aFjYPzUa zt5q^x!xJ5ca=fL;<5df4@djWjJ%x3}Q?(Vc6-}j%6-|9%~@xn80 zIDY{+S*=zV5}(fln0FlKDuAy5>@Yp>K_pMH11bC8S4y4meSe`>1K;--lu{=E+&4Yg zL39K}M6adnU-vxk-bRh$Hg(g39YjY!@qSLfbYa07tNBW$vOGRMei);_CNY3Z&1UmhUZ2YYiJ5dSuIv5};A7ji8!y`HFRAu}f*`mR zhT-b~P6PN6>2Aji;5#DvAPmDdo`rCLK98Vw@ta{Y0A^lC Zj{wqEF_aR=h%*2H002ovPDHLkV1m5T=Rp7f literal 0 HcmV?d00001 diff --git a/res/layout/account_item.xml b/res/layout/account_item.xml new file mode 100644 index 00000000000..1e3d6184311 --- /dev/null +++ b/res/layout/account_item.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/accounts_layout.xml b/res/layout/accounts_layout.xml new file mode 100644 index 00000000000..6934a9544d5 --- /dev/null +++ b/res/layout/accounts_layout.xml @@ -0,0 +1,37 @@ + + + + + + + + + + diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index 30cab8be2d7..77981c4f83f 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -190,7 +190,9 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { null, null); break; case R.id.drawer_menu_account_manage: - Toast.makeText(getApplicationContext(), "Not implemented yet", Toast.LENGTH_SHORT); + Intent manageAccountsIntent = new Intent(getApplicationContext(), + ManageAccountsActivity.class); + startActivity(manageAccountsIntent); break; case Menu.NONE: // account clicked @@ -271,8 +273,11 @@ public void updateAccountList() { AccountManager am = (AccountManager) this.getSystemService(this.ACCOUNT_SERVICE); // populate UI - repopulateAccountList(am.getAccountsByType(MainApp.getAccountType())); - setUsernameInDrawer(AccountUtils.getCurrentOwnCloudAccount(this)); + Account[] accounts = am.getAccountsByType(MainApp.getAccountType()); + if(accounts.length > 0) { + repopulateAccountList(accounts); + setUsernameInDrawer(AccountUtils.getCurrentOwnCloudAccount(this)); + } } /** diff --git a/src/com/owncloud/android/ui/activity/FileActivity.java b/src/com/owncloud/android/ui/activity/FileActivity.java index 33d0d301c92..2198a3a4849 100644 --- a/src/com/owncloud/android/ui/activity/FileActivity.java +++ b/src/com/owncloud/android/ui/activity/FileActivity.java @@ -579,4 +579,3 @@ public void onCancelCertificate() { } } -} diff --git a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java new file mode 100644 index 00000000000..8d384eb0e17 --- /dev/null +++ b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -0,0 +1,269 @@ +/** + * ownCloud Android client application + * + * @author Andy Scherzinger + * Copyright (C) 2016 ownCloud Inc. + *

+ * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.ui.activity; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.support.annotation.NonNull; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListView; + +import com.owncloud.android.MainApp; +import com.owncloud.android.R; +import com.owncloud.android.authentication.AccountUtils; +import com.owncloud.android.authentication.AuthenticatorActivity; +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.files.services.FileDownloader; +import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.services.OperationsService; +import com.owncloud.android.ui.adapter.AccountListAdapter; +import com.owncloud.android.ui.adapter.AccountListItem; +import com.owncloud.android.ui.helpers.FileOperationsHelper; + +import java.util.ArrayList; + +/** + * Managing the accounts. + */ +public class ManageAccountsActivity extends ToolbarActivity + implements AccountListAdapter.AccountListAdapterListener, AccountManagerCallback, ComponentsGetter { + private static final String TAG = ManageAccountsActivity.class.getSimpleName(); + + private ListView mListView; + private final Handler mHandler = new Handler(); + private String mAccountName; + private AccountListAdapter mAccountListAdapter; + protected FileUploader.FileUploaderBinder mUploaderBinder = null; + protected FileDownloader.FileDownloaderBinder mDownloaderBinder = null; + private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.accounts_layout); + + mListView = (ListView) findViewById(R.id.account_list); + + setupToolbar(); + updateActionBarTitleAndHomeButtonByString(getResources().getString(R.string.prefs_manage_accounts)); + + mAccountListAdapter = new AccountListAdapter(this, getAccountListItems()); + + mListView.setAdapter(mAccountListAdapter); + + initializeComponentGetters(); + } + + /** + * Initialize ComponentsGetters. + */ + private void initializeComponentGetters() { + mDownloadServiceConnection = newTransferenceServiceConnection(); + if (mDownloadServiceConnection != null) { + bindService(new Intent(this, FileDownloader.class), mDownloadServiceConnection, + Context.BIND_AUTO_CREATE); + } + mUploadServiceConnection = newTransferenceServiceConnection(); + if (mUploadServiceConnection != null) { + bindService(new Intent(this, FileUploader.class), mUploadServiceConnection, + Context.BIND_AUTO_CREATE); + } + } + + /** + * creates the account list items list including the add-account action in case multiaccount_support is enabled. + * + * @return list of account list items + */ + @NonNull + private ArrayList getAccountListItems() { + AccountManager am = (AccountManager) this.getSystemService(this.ACCOUNT_SERVICE); + Account[] accountList = am.getAccountsByType(MainApp.getAccountType()); + ArrayList adapterAccountList = new ArrayList(accountList.length); + for (Account account : accountList) { + adapterAccountList.add(new AccountListItem(account)); + } + + // Add Create Account item at the end of account list if multi-account is enabled + if (getResources().getBoolean(R.bool.multiaccount_support)) { + adapterAccountList.add(new AccountListItem()); + } + + return adapterAccountList; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + boolean retval = true; + switch (item.getItemId()) { + case android.R.id.home: + onBackPressed(); + break; + default: + retval = super.onOptionsItemSelected(item); + } + return retval; + } + + @Override + public void removeAccount(Account account) { + AccountManager am = (AccountManager) this.getSystemService(ACCOUNT_SERVICE); + mAccountName = account.name; + am.removeAccount(account, ManageAccountsActivity.this, mHandler); + Log_OC.d(TAG, "Remove an account " + account.name); + } + + @Override + public void changePasswordOfAccount(Account account) { + Intent updateAccountCredentials = new Intent(ManageAccountsActivity.this, AuthenticatorActivity.class); + updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account); + updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, + AuthenticatorActivity.ACTION_UPDATE_TOKEN); + startActivity(updateAccountCredentials); + } + + @Override + public void createAccount() { + // TODO Show create account screen if there isn't any account, probably via the drawer implementation + AccountManager am = AccountManager.get(getApplicationContext()); + am.addAccount(MainApp.getAccountType(), null, null, null, ManageAccountsActivity.this, null, null); + } + + @Override + public void run(AccountManagerFuture future) { + if (future.isDone()) { + // after remove account + Account account = new Account(mAccountName, MainApp.getAccountType()); + if (!AccountUtils.exists(account, MainApp.getAppContext())) { + // Cancel transfers of the removed account + if (mUploaderBinder != null) { + mUploaderBinder.cancel(account); + } + if (mDownloaderBinder != null) { + mDownloaderBinder.cancel(account); + } + } + + Account a = AccountUtils.getCurrentOwnCloudAccount(this); + String accountName = ""; + if (a == null) { + Account[] accounts = AccountManager.get(this) + .getAccountsByType(MainApp.getAccountType()); + if (accounts.length != 0) + accountName = accounts[0].name; + AccountUtils.setCurrentOwnCloudAccount(this, accountName); + } + + mAccountListAdapter = new AccountListAdapter(this, getAccountListItems()); + mAccountListAdapter.notifyDataSetChanged(); + } + } + + @Override + protected void onDestroy() { + if (mDownloadServiceConnection != null) { + unbindService(mDownloadServiceConnection); + mDownloadServiceConnection = null; + } + if (mUploadServiceConnection != null) { + unbindService(mUploadServiceConnection); + mUploadServiceConnection = null; + } + + super.onDestroy(); + } + + // Methods for ComponentsGetter + @Override + public FileDownloader.FileDownloaderBinder getFileDownloaderBinder() { + return mDownloaderBinder; + } + + @Override + public FileUploader.FileUploaderBinder getFileUploaderBinder() { + return mUploaderBinder; + } + + @Override + public OperationsService.OperationsServiceBinder getOperationsServiceBinder() { + return null; + } + + @Override + public FileDataStorageManager getStorageManager() { + return null; + } + + @Override + public FileOperationsHelper getFileOperationsHelper() { + return null; + } + + protected ServiceConnection newTransferenceServiceConnection() { + return new ManageAccountsServiceConnection(); + } + + /** + * Defines callbacks for service binding, passed to bindService() + */ + private class ManageAccountsServiceConnection implements ServiceConnection { + + @Override + public void onServiceConnected(ComponentName component, IBinder service) { + + if (component.equals(new ComponentName(ManageAccountsActivity.this, FileDownloader.class))) { + mDownloaderBinder = (FileDownloader.FileDownloaderBinder) service; + + } else if (component.equals(new ComponentName(ManageAccountsActivity.this, FileUploader.class))) { + Log_OC.d(TAG, "Upload service connected"); + mUploaderBinder = (FileUploader.FileUploaderBinder) service; + } else { + return; + } + + } + + @Override + public void onServiceDisconnected(ComponentName component) { + if (component.equals(new ComponentName(ManageAccountsActivity.this, FileDownloader.class))) { + Log_OC.d(TAG, "Download service suddenly disconnected"); + mDownloaderBinder = null; + } else if (component.equals(new ComponentName(ManageAccountsActivity.this, FileUploader.class))) { + Log_OC.d(TAG, "Upload service suddenly disconnected"); + mUploaderBinder = null; + } + } + } + + ; +} diff --git a/src/com/owncloud/android/ui/activity/Preferences.java b/src/com/owncloud/android/ui/activity/Preferences.java index 615978e0c67..ef870f694c5 100644 --- a/src/com/owncloud/android/ui/activity/Preferences.java +++ b/src/com/owncloud/android/ui/activity/Preferences.java @@ -92,25 +92,26 @@ * It proxies the necessary calls via {@link android.support.v7.app.AppCompatDelegate} to be used * with AppCompat. */ -public class Preferences extends PreferenceActivity - implements AccountManagerCallback, ComponentsGetter { +// TODO possible remove AccountManagerCallback and ComponentsGetter +public class Preferences extends PreferenceActivity { + //implements AccountManagerCallback, ComponentsGetter { private static final String TAG = Preferences.class.getSimpleName(); - private static final int ACTION_SELECT_UPLOAD_PATH = 1; private static final int ACTION_SELECT_UPLOAD_VIDEO_PATH = 2; private static final int ACTION_SELECT_SOURCE_PATH = 3; private static final int ACTION_REQUEST_PASSCODE = 5; private static final int ACTION_CONFIRM_PASSCODE = 6; + private DbHandler mDbHandler; private CheckBoxPreference pCode; private Preference pAboutApp; private AppCompatDelegate mDelegate; private PreferenceCategory mAccountsPrefCategory = null; - private final Handler mHandler = new Handler(); - private String mAccountName; + //private final Handler mHandler = new Handler(); + //private String mAccountName; private boolean mShowContextMenu = false; private String mUploadPath; @@ -127,9 +128,9 @@ public class Preferences extends PreferenceActivity private Preference mPrefInstantUploadSourcePath; private Preference mPrefInstantUploadBehaviour; - protected FileDownloader.FileDownloaderBinder mDownloaderBinder = null; - protected FileUploader.FileUploaderBinder mUploaderBinder = null; - private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null; + //protected FileDownloader.FileDownloaderBinder mDownloaderBinder = null; + //protected FileUploader.FileUploaderBinder mUploaderBinder = null; + //private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null; @SuppressWarnings("deprecation") @@ -138,6 +139,7 @@ public void onCreate(Bundle savedInstanceState) { getDelegate().installViewFactory(); getDelegate().onCreate(savedInstanceState); super.onCreate(savedInstanceState); + mDbHandler = new DbHandler(getBaseContext()); addPreferencesFromResource(R.xml.preferences); ActionBar actionBar = getSupportActionBar(); @@ -152,6 +154,7 @@ public void onCreate(Bundle savedInstanceState) { setContentDescription(getString(R.string.actionbar_settings)); } + // TODO remove accounts code - DONE // Load the accounts category for adding the list of accounts mAccountsPrefCategory = (PreferenceCategory) findPreference("accounts_category"); @@ -166,7 +169,7 @@ public boolean onItemLongClick(AdapterView parent, View view, int position, l if (obj != null && obj instanceof RadioButtonPreference) { mShowContextMenu = true; - mAccountName = ((RadioButtonPreference) obj).getKey(); + //mAccountName = ((RadioButtonPreference) obj).getKey(); String[] items = { getResources().getString(R.string.change_password), @@ -188,7 +191,8 @@ public void onItemClick(AdapterView parent, View view, int position, long id) AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE); Account accounts[] = am.getAccountsByType(MainApp.getAccountType()); for (Account a : accounts) { - if (a.name.equals(mAccountName)) { + //if (a.name.equals(mAccountName)) { + if (a.name.equals("")) { if (position==0) { // Change account password @@ -202,7 +206,8 @@ public void onItemClick(AdapterView parent, View view, int position, long id) } else if (position==1) { // Remove account - am.removeAccount(a, Preferences.this, mHandler); + //am.removeAccount(a, Preferences.this, mHandler); + am.removeAccount(a, null, new Handler()); Log_OC.d(TAG, "Remove an account " + a.name); alertDialog.cancel(); } @@ -298,8 +303,7 @@ public boolean onPreferenceClick(Preference preference) { String appName = getString(R.string.app_name); String downloadUrl = getString(R.string.url_app_download); - String recommendSubject = - String.format(getString(R.string.recommend_subject), + String recommendSubject = String.format(getString(R.string.recommend_subject), appName); String recommendText = String.format(getString(R.string.recommend_text), appName, downloadUrl); @@ -326,8 +330,7 @@ public boolean onPreferenceClick(Preference preference) { @Override public boolean onPreferenceClick(Preference preference) { String feedbackMail =(String) getText(R.string.mail_feedback); - String feedback =(String) getText(R.string.prefs_feedback) + - " - android v" + appVersion; + String feedback =(String) getText(R.string.prefs_feedback) + " - android v" + appVersion; Intent intent = new Intent(Intent.ACTION_SENDTO); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_SUBJECT, feedback); @@ -483,18 +486,18 @@ public boolean onPreferenceClick(Preference preference) { ((CheckBoxPreference)mPrefInstantUpload).isChecked()); /* About App */ - pAboutApp = (Preference) findPreference("about_app"); - if (pAboutApp != null) { - pAboutApp.setTitle(String.format(getString(R.string.about_android), - getString(R.string.app_name))); - pAboutApp.setSummary(String.format(getString(R.string.about_version), appVersion)); - } + pAboutApp = (Preference) findPreference("about_app"); + if (pAboutApp != null) { + pAboutApp.setTitle(String.format(getString(R.string.about_android), getString(R.string.app_name))); + pAboutApp.setSummary(String.format(getString(R.string.about_version), appVersion)); + } loadInstantUploadPath(); loadInstantUploadVideoPath(); loadInstantUploadSourcePath(); - /* ComponentsGetter */ + // TODO remove after test - DONE + /* ComponentsGetter mDownloadServiceConnection = newTransferenceServiceConnection(); if (mDownloadServiceConnection != null) { bindService(new Intent(this, FileDownloader.class), mDownloadServiceConnection, @@ -505,7 +508,7 @@ public boolean onPreferenceClick(Preference preference) { bindService(new Intent(this, FileUploader.class), mUploadServiceConnection, Context.BIND_AUTO_CREATE); } - +*/ } private void toggleInstantPictureOptions(Boolean value){ @@ -550,6 +553,8 @@ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuIn super.onCreateContextMenu(menu, v, menuInfo); } + // TODO remove, moved to account manager - DONE + /* @Override public void run(AccountManagerFuture future) { if (future.isDone()) { @@ -577,6 +582,7 @@ public void run(AccountManagerFuture future) { addAccountsCheckboxPreferences(); } } + */ @Override protected void onResume() { @@ -729,6 +735,10 @@ protected void onPostCreate(Bundle savedInstanceState) { @Override protected void onDestroy() { + mDbHandler.close(); + + // TODO remove after test - DONE + /* if (mDownloadServiceConnection != null) { unbindService(mDownloadServiceConnection); mDownloadServiceConnection = null; @@ -737,6 +747,7 @@ protected void onDestroy() { unbindService(mUploadServiceConnection); mUploadServiceConnection = null; } + */ super.onDestroy(); getDelegate().onDestroy(); @@ -761,6 +772,7 @@ private AppCompatDelegate getDelegate() { return mDelegate; } + // TODO remove/move after test /** * Create the list of accounts that has been added into the app */ @@ -858,6 +870,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { } } + // TODO remove after finished -DONE /** * Create the preference for allow adding new accounts */ @@ -962,7 +975,9 @@ private void saveInstantUploadSourcePathOnPreferences() { editor.commit(); } + //TODO remove this implementation // Methods for ComponetsGetter + /* @Override public FileDownloader.FileDownloaderBinder getFileDownloaderBinder() { return mDownloaderBinder; @@ -989,11 +1004,14 @@ public FileOperationsHelper getFileOperationsHelper() { return null; } + protected ServiceConnection newTransferenceServiceConnection() { return new PreferencesServiceConnection(); } + */ /** Defines callbacks for service binding, passed to bindService() */ + /* private class PreferencesServiceConnection implements ServiceConnection { @Override @@ -1002,8 +1020,7 @@ public void onServiceConnected(ComponentName component, IBinder service) { if (component.equals(new ComponentName(Preferences.this, FileDownloader.class))) { mDownloaderBinder = (FileDownloader.FileDownloaderBinder) service; - } else if (component.equals(new ComponentName(Preferences.this, - FileUploader.class))) { + } else if (component.equals(new ComponentName(Preferences.this, FileUploader.class))) { Log_OC.d(TAG, "Upload service connected"); mUploaderBinder = (FileUploader.FileUploaderBinder) service; } else { @@ -1017,11 +1034,11 @@ public void onServiceDisconnected(ComponentName component) { if (component.equals(new ComponentName(Preferences.this, FileDownloader.class))) { Log_OC.d(TAG, "Download service suddenly disconnected"); mDownloaderBinder = null; - } else if (component.equals(new ComponentName(Preferences.this, - FileUploader.class))) { + } else if (component.equals(new ComponentName(Preferences.this, FileUploader.class))) { Log_OC.d(TAG, "Upload service suddenly disconnected"); mUploaderBinder = null; } } }; + */ } diff --git a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java new file mode 100644 index 00000000000..15798d0649b --- /dev/null +++ b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java @@ -0,0 +1,161 @@ +/** + * ownCloud Android client application + * + * @author Andy Scherzinger + * Copyright (C) 2016 ownCloud Inc. + *

+ * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.ui.adapter; + +import android.accounts.Account; +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import com.owncloud.android.R; +import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.lib.resources.shares.OCShare; +import com.owncloud.android.ui.TextDrawable; +import com.owncloud.android.ui.activity.ManageAccountsActivity; +import com.owncloud.android.utils.BitmapUtils; + +import java.io.UnsupportedEncodingException; +import java.security.NoSuchAlgorithmException; +import java.util.List; + +/** + * This Adapter populates a ListView with all accounts within the app. + */ +public class AccountListAdapter extends ArrayAdapter { + private static final String TAG = AccountListAdapter.class.getSimpleName(); + + private final Context mContext; + private final List mValues; + private AccountListAdapterListener mListener; + + public AccountListAdapter(Context context, List values) { + super(context, -1, values); + this.mContext = context; + this.mValues = values; + this.mListener = (AccountListAdapterListener) context; + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + AccountViewHolderItem viewHolder; + + if (convertView == null) { + LayoutInflater inflater = ((ManageAccountsActivity) mContext).getLayoutInflater(); + convertView = inflater.inflate(R.layout.account_item, parent, false); + + viewHolder = new AccountViewHolderItem(); + viewHolder.textViewItem = (TextView) convertView.findViewById(R.id.user_name); + viewHolder.imageViewItem = (ImageView) convertView.findViewById(R.id.user_icon); + viewHolder.passwordButtonItem = (ImageView) convertView.findViewById(R.id.passwordButton); + viewHolder.removeButtonItem = (ImageView) convertView.findViewById(R.id.removeButton); + + convertView.setTag(viewHolder); + } else { + viewHolder = (AccountViewHolderItem) convertView.getTag(); + } + + AccountListItem accountListItem = mValues.get(position); + + + if (accountListItem != null) { + // create account item + if (AccountListItem.TYPE_ACCOUNT == accountListItem.getType()) { + Account account = accountListItem.getAccount(); + viewHolder.textViewItem.setText(account.name); + viewHolder.textViewItem.setTag(account.name); + + try { + int[] rgb = BitmapUtils.calculateRGB(account.name); + TextDrawable icon = new TextDrawable(account.name.substring(0, 1).toUpperCase() + , rgb[0], rgb[1], rgb[2]); + viewHolder.imageViewItem.setImageDrawable(icon); + } catch (Exception e) { + Log_OC.e(TAG, "Error calculating RGB value for account list item.", e); + // use user icon as a fallback + viewHolder.imageViewItem.setImageResource(R.drawable.ic_user); + } + + /// bind listener to change password + viewHolder.passwordButtonItem.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mListener.changePasswordOfAccount(mValues.get(position).getAccount()); + } + }); + + /// bind listener to remove account + viewHolder.removeButtonItem.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mListener.removeAccount(mValues.get(position).getAccount()); + mValues.remove(position); + AccountListAdapter.this.notifyDataSetChanged(); + } + }); + } // create add account action item + else if (AccountListItem.TYPE_ACTION_ADD == accountListItem.getType()) { + LayoutInflater inflater = ((ManageAccountsActivity) mContext).getLayoutInflater(); + View actionView = inflater.inflate(R.layout.account_item, parent, false); + ((TextView) actionView.findViewById(R.id.user_name)).setText(R.string.prefs_add_account); + ((ImageView) actionView.findViewById(R.id.user_icon)).setImageResource(R.drawable.ic_account_plus); + actionView.findViewById(R.id.passwordButton).setVisibility(View.GONE); + actionView.findViewById(R.id.removeButton).setVisibility(View.GONE); + + // bind action listener + actionView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mListener.createAccount(); + } + }); + + return actionView; + } + } + + return convertView; + } + + /** + * Listener interface for Activities using the {@link AccountListAdapter} + */ + public interface AccountListAdapterListener { + void removeAccount(Account account); + + void changePasswordOfAccount(Account account); + + void createAccount(); + } + + /** + * Account ViewHolderItem to get smooth scrolling. + */ + static class AccountViewHolderItem { + TextView textViewItem; + ImageView imageViewItem; + + ImageView passwordButtonItem; + ImageView removeButtonItem; + } +} diff --git a/src/com/owncloud/android/ui/adapter/AccountListItem.java b/src/com/owncloud/android/ui/adapter/AccountListItem.java new file mode 100644 index 00000000000..7677718ee0d --- /dev/null +++ b/src/com/owncloud/android/ui/adapter/AccountListItem.java @@ -0,0 +1,39 @@ +package com.owncloud.android.ui.adapter; + +import android.accounts.Account; + +/** + * Container implementation to add {@link Account}s and the add action to the list. + */ +public class AccountListItem { + public static final int TYPE_ACCOUNT = 0; + public static final int TYPE_ACTION_ADD = 1; + + private Account mAccount; + private int mType; + + /** + * creates an account list item containing an {@link Account}. + * + * @param account the account + */ + public AccountListItem(Account account) { + mAccount = account; + mType = TYPE_ACCOUNT; + } + + /** + * creates an account list item flagged as add-action. + */ + public AccountListItem() { + mType = TYPE_ACTION_ADD; + } + + public Account getAccount() { + return mAccount; + } + + public int getType() { + return mType; + } +} From 05b8580b5b3b7ad134d3aed16b0cc146d1d85748 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 24 Mar 2016 23:40:17 +0100 Subject: [PATCH 14/84] trying to detect account additions and change the list view content (not working yet) --- .../android/ui/activity/DrawerActivity.java | 3 +- .../ui/activity/ManageAccountsActivity.java | 63 ++++++++++++++++++- .../ui/adapter/AccountListAdapter.java | 6 +- 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index 77981c4f83f..08202efcd43 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -27,6 +27,7 @@ import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; +import android.os.Handler; import android.support.design.widget.NavigationView; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; @@ -519,7 +520,7 @@ private void createFirstAccount() { null, this, new AccountCreationCallback(), - null); + new Handler()); } /** diff --git a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java index 8d384eb0e17..11638412e8f 100644 --- a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -23,6 +23,7 @@ import android.accounts.AccountManager; import android.accounts.AccountManagerCallback; import android.accounts.AccountManagerFuture; +import android.accounts.OperationCanceledException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -154,9 +155,37 @@ public void changePasswordOfAccount(Account account) { @Override public void createAccount() { - // TODO Show create account screen if there isn't any account, probably via the drawer implementation AccountManager am = AccountManager.get(getApplicationContext()); - am.addAccount(MainApp.getAccountType(), null, null, null, ManageAccountsActivity.this, null, null); + am.addAccount(MainApp.getAccountType(), + null, + null, + null, + ManageAccountsActivity.this, + new AccountCreationCallback() { + @Override + public void run(AccountManagerFuture future) { + if (future != null) { + try { + Bundle result = future.getResult(); + String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); + AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name); + ArrayList accounts = getAccountListItems(); + mAccountListAdapter.clear(); + mAccountListAdapter.addAll(accounts); + mAccountListAdapter.notifyDataSetChanged(); + mListView.invalidate(); + } catch (OperationCanceledException e) { + Log_OC.d(TAG, "Account creation canceled"); + } catch (Exception e) { + Log_OC.e(TAG, "Account creation finished in exception: ", e); + } + + } else { + Log_OC.e(TAG, "Account creation callback with null bundle"); + } + } + }, + mHandler); } @Override @@ -189,6 +218,36 @@ public void run(AccountManagerFuture future) { } } + /** + * Helper class handling a callback from the {@link AccountManager} after the creation of + * a new ownCloud {@link Account} finished, successfully or not. + *

+ * At this moment, only called after the creation of the first account. + */ + public class AccountCreationCallback implements AccountManagerCallback { + @Override + public void run(AccountManagerFuture future) { + if (future != null) { + try { + Bundle result = future.getResult(); + String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); + AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name); + ArrayList accounts = getAccountListItems(); + ManageAccountsActivity.this.mAccountListAdapter = new AccountListAdapter(ManageAccountsActivity + .this, accounts); + ManageAccountsActivity.this.mAccountListAdapter.notifyDataSetChanged(); + } catch (OperationCanceledException e) { + Log_OC.d(TAG, "Account creation canceled"); + } catch (Exception e) { + Log_OC.e(TAG, "Account creation finished in exception: ", e); + } + + } else { + Log_OC.e(TAG, "Account creation callback with null bundle"); + } + } + } + @Override protected void onDestroy() { if (mDownloadServiceConnection != null) { diff --git a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java index 15798d0649b..973d5f9669d 100644 --- a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java @@ -46,7 +46,7 @@ public class AccountListAdapter extends ArrayAdapter { private static final String TAG = AccountListAdapter.class.getSimpleName(); private final Context mContext; - private final List mValues; + private List mValues; private AccountListAdapterListener mListener; public AccountListAdapter(Context context, List values) { @@ -56,6 +56,10 @@ public AccountListAdapter(Context context, List values) { this.mListener = (AccountListAdapterListener) context; } + public void setAccountList(List values) { + this.mValues = values; + } + @Override public View getView(final int position, View convertView, ViewGroup parent) { AccountViewHolderItem viewHolder; From fab46611f0621d4fd64d2dce144d9b4ea5ee904d Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 24 Mar 2016 23:57:56 +0100 Subject: [PATCH 15/84] minor changes for testing --- .../android/ui/activity/ManageAccountsActivity.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java index 11638412e8f..3bd20cc9271 100644 --- a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -156,12 +156,12 @@ public void changePasswordOfAccount(Account account) { @Override public void createAccount() { AccountManager am = AccountManager.get(getApplicationContext()); - am.addAccount(MainApp.getAccountType(), + final AccountManagerFuture future = am.addAccount(MainApp.getAccountType(), null, null, null, - ManageAccountsActivity.this, - new AccountCreationCallback() { + this, + new AccountManagerCallback() { @Override public void run(AccountManagerFuture future) { if (future != null) { @@ -173,19 +173,14 @@ public void run(AccountManagerFuture future) { mAccountListAdapter.clear(); mAccountListAdapter.addAll(accounts); mAccountListAdapter.notifyDataSetChanged(); - mListView.invalidate(); } catch (OperationCanceledException e) { Log_OC.d(TAG, "Account creation canceled"); } catch (Exception e) { Log_OC.e(TAG, "Account creation finished in exception: ", e); } - - } else { - Log_OC.e(TAG, "Account creation callback with null bundle"); } } - }, - mHandler); + }, mHandler); } @Override From a7707142808e400d05488e980ec4c515fb9c0589 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Fri, 25 Mar 2016 00:01:49 +0100 Subject: [PATCH 16/84] next try --- .../android/ui/activity/ManageAccountsActivity.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java index 3bd20cc9271..60b3175a9bb 100644 --- a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -172,7 +172,12 @@ public void run(AccountManagerFuture future) { ArrayList accounts = getAccountListItems(); mAccountListAdapter.clear(); mAccountListAdapter.addAll(accounts); - mAccountListAdapter.notifyDataSetChanged(); + runOnUiThread(new Runnable() { + @Override + public void run() { + mAccountListAdapter.notifyDataSetChanged(); + } + }); } catch (OperationCanceledException e) { Log_OC.d(TAG, "Account creation canceled"); } catch (Exception e) { From 963fefbe0c6b8ce7242acc9ea6d8f84d49ac58da Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Fri, 25 Mar 2016 00:13:48 +0100 Subject: [PATCH 17/84] proper list rendering upon account add/remove --- .../ui/activity/ManageAccountsActivity.java | 38 ++----------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java index 60b3175a9bb..6ca8de810d9 100644 --- a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -156,7 +156,7 @@ public void changePasswordOfAccount(Account account) { @Override public void createAccount() { AccountManager am = AccountManager.get(getApplicationContext()); - final AccountManagerFuture future = am.addAccount(MainApp.getAccountType(), + am.addAccount(MainApp.getAccountType(), null, null, null, @@ -169,9 +169,9 @@ public void run(AccountManagerFuture future) { Bundle result = future.getResult(); String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name); - ArrayList accounts = getAccountListItems(); - mAccountListAdapter.clear(); - mAccountListAdapter.addAll(accounts); + mAccountListAdapter = new AccountListAdapter(ManageAccountsActivity + .this, getAccountListItems()); + mListView.setAdapter(mAccountListAdapter); runOnUiThread(new Runnable() { @Override public void run() { @@ -218,36 +218,6 @@ public void run(AccountManagerFuture future) { } } - /** - * Helper class handling a callback from the {@link AccountManager} after the creation of - * a new ownCloud {@link Account} finished, successfully or not. - *

- * At this moment, only called after the creation of the first account. - */ - public class AccountCreationCallback implements AccountManagerCallback { - @Override - public void run(AccountManagerFuture future) { - if (future != null) { - try { - Bundle result = future.getResult(); - String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); - AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name); - ArrayList accounts = getAccountListItems(); - ManageAccountsActivity.this.mAccountListAdapter = new AccountListAdapter(ManageAccountsActivity - .this, accounts); - ManageAccountsActivity.this.mAccountListAdapter.notifyDataSetChanged(); - } catch (OperationCanceledException e) { - Log_OC.d(TAG, "Account creation canceled"); - } catch (Exception e) { - Log_OC.e(TAG, "Account creation finished in exception: ", e); - } - - } else { - Log_OC.e(TAG, "Account creation callback with null bundle"); - } - } - } - @Override protected void onDestroy() { if (mDownloadServiceConnection != null) { From 1d7a36aa16e13d9311f51b348aeb3eddf2dd53c3 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Fri, 25 Mar 2016 00:50:22 +0100 Subject: [PATCH 18/84] if new account is created via drawer menu item click, refresh drawer account list and active account --- .../owncloud/android/ui/activity/DrawerActivity.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index 08202efcd43..f8b40481fa8 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -186,9 +186,7 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { startActivity(settingsIntent); break; case R.id.drawer_menu_account_add: - AccountManager am = AccountManager.get(getApplicationContext()); - am.addAccount(MainApp.getAccountType(), null, null, null, DrawerActivity.this, - null, null); + createAccount(); break; case R.id.drawer_menu_account_manage: Intent manageAccountsIntent = new Intent(getApplicationContext(), @@ -497,7 +495,7 @@ protected void swapToDefaultAccount() { Account newAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()); if (newAccount == null) { /// no account available: force account creation - createFirstAccount(); + createAccount(); mRedirectingToSetupAccount = true; mAccountWasSet = false; mAccountWasRestored = false; @@ -512,7 +510,7 @@ protected void swapToDefaultAccount() { /** * Launches the account creation activity. To use when no ownCloud account is available. */ - private void createFirstAccount() { + private void createAccount() { AccountManager am = AccountManager.get(getApplicationContext()); am.addAccount(MainApp.getAccountType(), null, @@ -545,6 +543,8 @@ public void run(AccountManagerFuture future) { setAccount(new Account(name, type), false); accountWasSet = true; } + + DrawerActivity.this.updateAccountList(); } catch (OperationCanceledException e) { Log_OC.d(TAG, "Account creation canceled"); From bab107c9b1cc7827ecd62edd47573cc5c0bafeb1 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Fri, 25 Mar 2016 01:21:56 +0100 Subject: [PATCH 19/84] missing license comment in class --- .../android/ui/adapter/AccountListItem.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/com/owncloud/android/ui/adapter/AccountListItem.java b/src/com/owncloud/android/ui/adapter/AccountListItem.java index 7677718ee0d..bd7c28ec5d7 100644 --- a/src/com/owncloud/android/ui/adapter/AccountListItem.java +++ b/src/com/owncloud/android/ui/adapter/AccountListItem.java @@ -1,3 +1,22 @@ +/** + * ownCloud Android client application + * + * @author Andy Scherzinger + * Copyright (C) 2016 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package com.owncloud.android.ui.adapter; import android.accounts.Account; From afbc964358480d5f182a99e8e475f37ad96e93e2 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Fri, 25 Mar 2016 13:13:58 +0100 Subject: [PATCH 20/84] housekeeping Preferences, removed all accounts related code --- res/xml/preferences.xml | 7 +- .../ui/activity/ManageAccountsActivity.java | 1 + .../android/ui/activity/Preferences.java | 367 +----------------- .../ui/adapter/AccountListAdapter.java | 20 +- 4 files changed, 25 insertions(+), 370 deletions(-) diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index 75ea3289e8a..a3309a59c6a 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -17,12 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - - - + , ComponentsGetter { private static final String TAG = Preferences.class.getSimpleName(); @@ -110,9 +91,6 @@ public class Preferences extends PreferenceActivity { private AppCompatDelegate mDelegate; private PreferenceCategory mAccountsPrefCategory = null; - //private final Handler mHandler = new Handler(); - //private String mAccountName; - private boolean mShowContextMenu = false; private String mUploadPath; private String mUploadVideoPath; @@ -128,11 +106,6 @@ public class Preferences extends PreferenceActivity { private Preference mPrefInstantUploadSourcePath; private Preference mPrefInstantUploadBehaviour; - //protected FileDownloader.FileDownloaderBinder mDownloaderBinder = null; - //protected FileUploader.FileUploaderBinder mUploaderBinder = null; - //private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null; - - @SuppressWarnings("deprecation") @Override public void onCreate(Bundle savedInstanceState) { @@ -154,76 +127,6 @@ public void onCreate(Bundle savedInstanceState) { setContentDescription(getString(R.string.actionbar_settings)); } - // TODO remove accounts code - DONE - // Load the accounts category for adding the list of accounts - mAccountsPrefCategory = (PreferenceCategory) findPreference("accounts_category"); - - ListView listView = getListView(); - listView.setFilterTouchesWhenObscured(true); - listView.setOnItemLongClickListener(new OnItemLongClickListener() { - @Override - public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { - ListView listView = (ListView) parent; - ListAdapter listAdapter = listView.getAdapter(); - Object obj = listAdapter.getItem(position); - - if (obj != null && obj instanceof RadioButtonPreference) { - mShowContextMenu = true; - //mAccountName = ((RadioButtonPreference) obj).getKey(); - - String[] items = { - getResources().getString(R.string.change_password), - getResources().getString(R.string.delete_account) - }; - final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(Preferences.this); - View convertView = getLayoutInflater().inflate(R.layout.alert_dialog_list_view, null); - alertDialogBuilder.setView(convertView); - ListView lv = (ListView) convertView.findViewById(R.id.list); - ArrayAdapter adapter = new ArrayAdapter( - Preferences.this,R.layout.simple_dialog_list_item,items); - lv.setAdapter(adapter); - - //Setup proper inline listener - final AlertDialog alertDialog = alertDialogBuilder.create(); - lv.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE); - Account accounts[] = am.getAccountsByType(MainApp.getAccountType()); - for (Account a : accounts) { - //if (a.name.equals(mAccountName)) { - if (a.name.equals("")) { - if (position==0) { - - // Change account password - Intent updateAccountCredentials = new Intent(Preferences.this, AuthenticatorActivity.class); - updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, a); - updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, - AuthenticatorActivity.ACTION_UPDATE_TOKEN); - startActivity(updateAccountCredentials); - alertDialog.cancel(); - - } else if (position==1) { - - // Remove account - //am.removeAccount(a, Preferences.this, mHandler); - am.removeAccount(a, null, new Handler()); - Log_OC.d(TAG, "Remove an account " + a.name); - alertDialog.cancel(); - } - } - } - } - }); - alertDialog.show(); - - View.OnLongClickListener longListener = (View.OnLongClickListener) obj; - return longListener.onLongClick(view); - } - return false; - } - }); - // Load package info String temp; try { @@ -247,22 +150,19 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { Boolean incoming = (Boolean) newValue; i.setAction( - incoming.booleanValue() ? PassCodeActivity.ACTION_REQUEST_WITH_RESULT : + incoming ? PassCodeActivity.ACTION_REQUEST_WITH_RESULT : PassCodeActivity.ACTION_CHECK_WITH_RESULT ); - startActivityForResult(i, incoming.booleanValue() ? ACTION_REQUEST_PASSCODE : + startActivityForResult(i, incoming ? ACTION_REQUEST_PASSCODE : ACTION_CONFIRM_PASSCODE); // Don't update just yet, we will decide on it in onActivityResult return false; } }); - } - - PreferenceCategory preferenceCategory = (PreferenceCategory) findPreference("more"); boolean helpEnabled = getResources().getBoolean(R.bool.help_enabled); @@ -284,7 +184,6 @@ public boolean onPreferenceClick(Preference preference) { } else { preferenceCategory.removePreference(pHelp); } - } boolean recommendEnabled = getResources().getBoolean(R.bool.recommend_enabled); @@ -319,7 +218,6 @@ public boolean onPreferenceClick(Preference preference) { } else { preferenceCategory.removePreference(pRecommend); } - } boolean feedbackEnabled = getResources().getBoolean(R.bool.feedback_enabled); @@ -496,19 +394,6 @@ public boolean onPreferenceClick(Preference preference) { loadInstantUploadVideoPath(); loadInstantUploadSourcePath(); - // TODO remove after test - DONE - /* ComponentsGetter - mDownloadServiceConnection = newTransferenceServiceConnection(); - if (mDownloadServiceConnection != null) { - bindService(new Intent(this, FileDownloader.class), mDownloadServiceConnection, - Context.BIND_AUTO_CREATE); - } - mUploadServiceConnection = newTransferenceServiceConnection(); - if (mUploadServiceConnection != null) { - bindService(new Intent(this, FileUploader.class), mUploadServiceConnection, - Context.BIND_AUTO_CREATE); - } -*/ } private void toggleInstantPictureOptions(Boolean value){ @@ -541,49 +426,6 @@ private void toggleInstantUploadCommonOptions(Boolean video, Boolean picture){ } } - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - - // Filter for only showing contextual menu when long press on the - // accounts - if (mShowContextMenu) { - getMenuInflater().inflate(R.menu.account_picker_long_click, menu); - mShowContextMenu = false; - } - super.onCreateContextMenu(menu, v, menuInfo); - } - - // TODO remove, moved to account manager - DONE - /* - @Override - public void run(AccountManagerFuture future) { - if (future.isDone()) { - // after remove account - Account account = new Account(mAccountName, MainApp.getAccountType()); - if (!AccountUtils.exists(account, MainApp.getAppContext())) { - // Cancel tranfers - if (mUploaderBinder != null) { - mUploaderBinder.cancel(account); - } - if (mDownloaderBinder != null) { - mDownloaderBinder.cancel(account); - } - } - - Account a = AccountUtils.getCurrentOwnCloudAccount(this); - String accountName = ""; - if (a == null) { - Account[] accounts = AccountManager.get(this) - .getAccountsByType(MainApp.getAccountType()); - if (accounts.length != 0) - accountName = accounts[0].name; - AccountUtils.setCurrentOwnCloudAccount(this, accountName); - } - addAccountsCheckboxPreferences(); - } - } - */ - @Override protected void onResume() { super.onResume(); @@ -591,9 +433,6 @@ protected void onResume() { PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); boolean state = appPrefs.getBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, false); pCode.setChecked(state); - - // Populate the accounts category with the list of accounts - addAccountsCheckboxPreferences(); } @Override @@ -737,18 +576,6 @@ protected void onPostCreate(Bundle savedInstanceState) { protected void onDestroy() { mDbHandler.close(); - // TODO remove after test - DONE - /* - if (mDownloadServiceConnection != null) { - unbindService(mDownloadServiceConnection); - mDownloadServiceConnection = null; - } - if (mUploadServiceConnection != null) { - unbindService(mUploadServiceConnection); - mUploadServiceConnection = null; - } - */ - super.onDestroy(); getDelegate().onDestroy(); } @@ -772,126 +599,6 @@ private AppCompatDelegate getDelegate() { return mDelegate; } - // TODO remove/move after test - /** - * Create the list of accounts that has been added into the app - */ - @SuppressWarnings("deprecation") - private void addAccountsCheckboxPreferences() { - - // Remove accounts in case list is refreshing for avoiding to have - // duplicate items - if (mAccountsPrefCategory.getPreferenceCount() > 0) { - mAccountsPrefCategory.removeAll(); - } - - AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE); - Account accounts[] = am.getAccountsByType(MainApp.getAccountType()); - Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()); - - if (am.getAccountsByType(MainApp.getAccountType()).length == 0) { - // Show create account screen if there isn't any account - am.addAccount(MainApp.getAccountType(), null, null, null, this, - null, - null); - } - else { - OwnCloudAccount oca; - for (Account a : accounts) { - RadioButtonPreference accountPreference = new RadioButtonPreference(this); - accountPreference.setKey(a.name); - try { - oca = new OwnCloudAccount(a, this); - accountPreference.setTitle( - oca.getDisplayName() + " @ " + - DisplayUtils.convertIdn(a.name.substring(a.name.lastIndexOf("@") + 1), false) - ); - } catch (Exception e) { - Log_OC.w( - TAG, - "Account not found right after being read :\\ ; using account name instead of display name" - ); - // Handle internationalized domain names - accountPreference.setTitle(DisplayUtils.convertIdn(a.name, false)); - } - mAccountsPrefCategory.addPreference(accountPreference); - - // Check the current account that is being used - if (a.name.equals(currentAccount.name)) { - accountPreference.setChecked(true); - } else { - accountPreference.setChecked(false); - } - - accountPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - String key = preference.getKey(); - AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE); - Account accounts[] = am.getAccountsByType(MainApp.getAccountType()); - for (Account a : accounts) { - RadioButtonPreference p = - (RadioButtonPreference) findPreference(a.name); - if (key.equals(a.name)) { - boolean accountChanged = !p.isChecked(); - p.setChecked(true); - AccountUtils.setCurrentOwnCloudAccount( - getApplicationContext(), - a.name - ); - if (accountChanged) { - // restart the main activity - Intent i = new Intent( - Preferences.this, - FileDisplayActivity.class - ); - i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - startActivity(i); - } else { - finish(); - } - } else { - p.setChecked(false); - } - } - return (Boolean) newValue; - } - }); - - } - - // Add Create Account preference at the end of account list if - // Multiaccount is enabled - if (getResources().getBoolean(R.bool.multiaccount_support)) { - createAddAccountPreference(); - } - - } - } - - // TODO remove after finished -DONE - /** - * Create the preference for allow adding new accounts - */ - private void createAddAccountPreference() { - Preference addAccountPref = new Preference(this); - addAccountPref.setKey("add_account"); - addAccountPref.setTitle(getString(R.string.prefs_add_account)); - mAccountsPrefCategory.addPreference(addAccountPref); - - addAccountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - AccountManager am = AccountManager.get(getApplicationContext()); - am.addAccount(MainApp.getAccountType(), null, null, null, Preferences.this, - null, null); - return true; - } - }); - - } - /** * Load upload path set on preferences */ @@ -975,70 +682,4 @@ private void saveInstantUploadSourcePathOnPreferences() { editor.commit(); } - //TODO remove this implementation - // Methods for ComponetsGetter - /* - @Override - public FileDownloader.FileDownloaderBinder getFileDownloaderBinder() { - return mDownloaderBinder; - } - - - @Override - public FileUploader.FileUploaderBinder getFileUploaderBinder() { - return mUploaderBinder; - } - - @Override - public OperationsService.OperationsServiceBinder getOperationsServiceBinder() { - return null; - } - - @Override - public FileDataStorageManager getStorageManager() { - return null; - } - - @Override - public FileOperationsHelper getFileOperationsHelper() { - return null; - } - - - protected ServiceConnection newTransferenceServiceConnection() { - return new PreferencesServiceConnection(); - } - */ - - /** Defines callbacks for service binding, passed to bindService() */ - /* - private class PreferencesServiceConnection implements ServiceConnection { - - @Override - public void onServiceConnected(ComponentName component, IBinder service) { - - if (component.equals(new ComponentName(Preferences.this, FileDownloader.class))) { - mDownloaderBinder = (FileDownloader.FileDownloaderBinder) service; - - } else if (component.equals(new ComponentName(Preferences.this, FileUploader.class))) { - Log_OC.d(TAG, "Upload service connected"); - mUploaderBinder = (FileUploader.FileUploaderBinder) service; - } else { - return; - } - - } - - @Override - public void onServiceDisconnected(ComponentName component) { - if (component.equals(new ComponentName(Preferences.this, FileDownloader.class))) { - Log_OC.d(TAG, "Download service suddenly disconnected"); - mDownloaderBinder = null; - } else if (component.equals(new ComponentName(Preferences.this, FileUploader.class))) { - Log_OC.d(TAG, "Upload service suddenly disconnected"); - mUploaderBinder = null; - } - } - }; - */ } diff --git a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java index 973d5f9669d..e499d661988 100644 --- a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java @@ -29,11 +29,13 @@ import android.widget.TextView; import com.owncloud.android.R; +import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.ui.TextDrawable; import com.owncloud.android.ui.activity.ManageAccountsActivity; import com.owncloud.android.utils.BitmapUtils; +import com.owncloud.android.utils.DisplayUtils; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; @@ -86,7 +88,23 @@ public View getView(final int position, View convertView, ViewGroup parent) { // create account item if (AccountListItem.TYPE_ACCOUNT == accountListItem.getType()) { Account account = accountListItem.getAccount(); - viewHolder.textViewItem.setText(account.name); + try { + OwnCloudAccount oca = new OwnCloudAccount(account, mContext); + viewHolder.textViewItem.setText( + oca.getDisplayName() + " @ " + + DisplayUtils.convertIdn( + account.name.substring(account.name.lastIndexOf("@") + 1), + false + ) + ); + } catch (Exception e) { + Log_OC.w( + TAG, + "Account not found right after being read :\\ ; using account name instead of display name" + ); + // Handle internationalized domain names + viewHolder.textViewItem.setText(DisplayUtils.convertIdn(account.name, false)); + } viewHolder.textViewItem.setTag(account.name); try { From 099a63d02e65449ee34f81174f99c2a943222b00 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Fri, 25 Mar 2016 18:45:31 +0100 Subject: [PATCH 21/84] change detection for the drawer implementation --- .../android/ui/activity/DrawerActivity.java | 44 ++++++++--- .../ui/activity/ManageAccountsActivity.java | 73 +++++++++++++++---- 2 files changed, 94 insertions(+), 23 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index f8b40481fa8..2ca2fa21fdc 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -57,6 +57,7 @@ public abstract class DrawerActivity extends ToolbarActivity { private static final String TAG = DrawerActivity.class.getSimpleName(); private static final String KEY_IS_ACCOUNT_CHOOSER_ACTIVE = "IS_ACCOUNT_CHOOSER_ACTIVE"; + private static final int ACTION_MANAGE_ACCOUNTS = 101; /** * Reference to the drawer layout. @@ -191,7 +192,7 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { case R.id.drawer_menu_account_manage: Intent manageAccountsIntent = new Intent(getApplicationContext(), ManageAccountsActivity.class); - startActivity(manageAccountsIntent); + startActivityForResult(manageAccountsIntent, ACTION_MANAGE_ACCOUNTS); break; case Menu.NONE: // account clicked @@ -267,12 +268,8 @@ public void setDrawerIndicatorEnabled(boolean enable) { /** * updates the account list in the drawer. */ - // TODO call updateAccountList() after n.o. accounts changed public void updateAccountList() { - AccountManager am = (AccountManager) this.getSystemService(this.ACCOUNT_SERVICE); - - // populate UI - Account[] accounts = am.getAccountsByType(MainApp.getAccountType()); + Account[] accounts = AccountManager.get(this).getAccountsByType(MainApp.getAccountType()); if(accounts.length > 0) { repopulateAccountList(accounts); setUsernameInDrawer(AccountUtils.getCurrentOwnCloudAccount(this)); @@ -286,7 +283,7 @@ public void updateAccountList() { */ private void repopulateAccountList(Account[] accounts) { // remove all accounts from list - mNavigationView.getMenu().removeItem(Menu.NONE); + mNavigationView.getMenu().removeGroup(R.id.drawer_menu_accounts); // add all accounts to list for (int i = 0; i < accounts.length; i++) { @@ -294,14 +291,20 @@ private void repopulateAccountList(Account[] accounts) { int[] rgb = BitmapUtils.calculateRGB(accounts[i].name); TextDrawable icon = new TextDrawable(accounts[i].name.substring(0, 1).toUpperCase() , rgb[0], rgb[1], rgb[2]); - mNavigationView.getMenu().add(R.id.drawer_menu_accounts, Menu.NONE, 0, accounts[i].name).setIcon(icon); + mNavigationView.getMenu().add(R.id.drawer_menu_accounts, Menu.NONE, 1, accounts[i].name).setIcon(icon); } catch (Exception e) { Log_OC.e(TAG, "Error calculating RGB value for account menu item.", e); - mNavigationView.getMenu().add(R.id.drawer_menu_accounts, Menu.NONE, 0, accounts[i].name).setIcon(R + mNavigationView.getMenu().add(R.id.drawer_menu_accounts, Menu.NONE, 1, accounts[i].name).setIcon(R .drawable.ic_account_circle); } } + // re-add add-account and manage-accounts + mNavigationView.getMenu().add(R.id.drawer_menu_accounts, R.id.drawer_menu_account_add, 2, + getResources().getString(R.string.prefs_add_account)).setIcon(R.drawable.ic_account_plus); + mNavigationView.getMenu().add(R.id.drawer_menu_accounts, R.id.drawer_menu_account_manage, 2, + getResources().getString(R.string.drawer_manage_accounts)).setIcon(R.drawable.ic_settings); + // adding sets menu group back to visible, so safety check and setting invisible showMenu(); } @@ -441,6 +444,27 @@ public void onBackPressed() { super.onBackPressed(); } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + // update Account list and active account if Manage Account activity replies with + // - ACCOUNT_LIST_CHANGED = true + // - RESULT_OK + if (requestCode == ACTION_MANAGE_ACCOUNTS + && resultCode == RESULT_OK + && data.getBooleanExtra(ManageAccountsActivity.KEY_ACCOUNT_LIST_CHANGED, false)) { + + // current account has changed + if(data.getBooleanExtra(ManageAccountsActivity.KEY_CURRENT_ACCOUNT_CHANGED, false)) { + mCurrentAccount = AccountUtils.getCurrentOwnCloudAccount(this); + restart(); + } else { + updateAccountList(); + } + } + } + /** * Finds a view that was identified by the id attribute from the drawer header. * @@ -451,6 +475,7 @@ private View findNavigationViewChildById(int id) { return ((NavigationView) findViewById(R.id.nav_view)).getHeaderView(0).findViewById(id); } + // TODO call on current account changed public void restart() { Intent i = new Intent(this, FileDisplayActivity.class); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -545,6 +570,7 @@ public void run(AccountManagerFuture future) { } DrawerActivity.this.updateAccountList(); + DrawerActivity.this.restart(); } catch (OperationCanceledException e) { Log_OC.d(TAG, "Account creation canceled"); diff --git a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java index 5a44661bd7b..20c860df733 100644 --- a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -31,10 +31,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; -import android.support.annotation.NonNull; import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; import android.widget.ListView; import com.owncloud.android.MainApp; @@ -51,13 +48,17 @@ import com.owncloud.android.ui.helpers.FileOperationsHelper; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; /** - * Managing the accounts. + * An Activity that allows the user to manage accounts. */ public class ManageAccountsActivity extends ToolbarActivity implements AccountListAdapter.AccountListAdapterListener, AccountManagerCallback, ComponentsGetter { private static final String TAG = ManageAccountsActivity.class.getSimpleName(); + public static final String KEY_ACCOUNT_LIST_CHANGED = "ACCOUNT_LIST_CHANGED"; + public static final String KEY_CURRENT_ACCOUNT_CHANGED = "CURRENT_ACCOUNT_CHANGED"; private ListView mListView; private final Handler mHandler = new Handler(); @@ -66,6 +67,8 @@ public class ManageAccountsActivity extends ToolbarActivity protected FileUploader.FileUploaderBinder mUploaderBinder = null; protected FileDownloader.FileDownloaderBinder mDownloaderBinder = null; private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null; + Set mOriginalAccounts; + String mOriginalCurrentAccount; @Override protected void onCreate(Bundle savedInstanceState) { @@ -79,6 +82,10 @@ protected void onCreate(Bundle savedInstanceState) { setupToolbar(); updateActionBarTitleAndHomeButtonByString(getResources().getString(R.string.prefs_manage_accounts)); + Account[] accountList = AccountManager.get(this).getAccountsByType(MainApp.getAccountType()); + mOriginalAccounts = toAccountNameSet(accountList); + mOriginalCurrentAccount = AccountUtils.getCurrentOwnCloudAccount(this).name; + mAccountListAdapter = new AccountListAdapter(this, getAccountListItems()); mListView.setAdapter(mAccountListAdapter); @@ -86,6 +93,52 @@ protected void onCreate(Bundle savedInstanceState) { initializeComponentGetters(); } + /** + * converts an array of accounts into a set of account names. + * + * @param accountList the account array + * @return set of account names + */ + private Set toAccountNameSet(Account[] accountList) { + Set actualAccounts = new HashSet(accountList.length); + for (Account account : accountList) { + actualAccounts.add(account.name); + } + return actualAccounts; + } + + @Override + public void onBackPressed() { + Intent resultIntent = new Intent(); + resultIntent.putExtra(KEY_ACCOUNT_LIST_CHANGED, hasAccountListChanged()); + resultIntent.putExtra(KEY_CURRENT_ACCOUNT_CHANGED, hasCurrentAccountChanged()); + setResult(RESULT_OK, resultIntent); + + finish(); + super.onBackPressed(); + } + + /** + * checks the set of actual accounts against the set of original accounts when the activity has been started. + * + * @return true if aacount list has changed, false if not + */ + private boolean hasAccountListChanged() { + Account[] accountList = AccountManager.get(this).getAccountsByType(MainApp.getAccountType()); + Set actualAccounts = toAccountNameSet(accountList); + return !mOriginalAccounts.equals(actualAccounts); + } + + /** + * checks actual current account against current accounts when the activity has been started. + * + * @return true if aacount list has changed, false if not + */ + private boolean hasCurrentAccountChanged() { + String currentAccount = AccountUtils.getCurrentOwnCloudAccount(this).name; + return !mOriginalCurrentAccount.equals(currentAccount); + } + /** * Initialize ComponentsGetters. */ @@ -107,10 +160,8 @@ private void initializeComponentGetters() { * * @return list of account list items */ - @NonNull private ArrayList getAccountListItems() { - AccountManager am = (AccountManager) this.getSystemService(this.ACCOUNT_SERVICE); - Account[] accountList = am.getAccountsByType(MainApp.getAccountType()); + Account[] accountList = AccountManager.get(this).getAccountsByType(MainApp.getAccountType()); ArrayList adapterAccountList = new ArrayList(accountList.length); for (Account account : accountList) { adapterAccountList.add(new AccountListItem(account)); @@ -207,8 +258,7 @@ public void run(AccountManagerFuture future) { Account a = AccountUtils.getCurrentOwnCloudAccount(this); String accountName = ""; if (a == null) { - Account[] accounts = AccountManager.get(this) - .getAccountsByType(MainApp.getAccountType()); + Account[] accounts = AccountManager.get(this).getAccountsByType(MainApp.getAccountType()); if (accounts.length != 0) accountName = accounts[0].name; AccountUtils.setCurrentOwnCloudAccount(this, accountName); @@ -277,10 +327,7 @@ public void onServiceConnected(ComponentName component, IBinder service) { } else if (component.equals(new ComponentName(ManageAccountsActivity.this, FileUploader.class))) { Log_OC.d(TAG, "Upload service connected"); mUploaderBinder = (FileUploader.FileUploaderBinder) service; - } else { - return; } - } @Override @@ -294,6 +341,4 @@ public void onServiceDisconnected(ComponentName component) { } } } - - ; } From ae2256801670ba1f9f7aefd27aaeeae5ebcd66f1 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Fri, 25 Mar 2016 19:16:42 +0100 Subject: [PATCH 22/84] added single top flag --- src/com/owncloud/android/ui/activity/DrawerActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index 2ca2fa21fdc..baf7d366601 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -475,10 +475,10 @@ private View findNavigationViewChildById(int id) { return ((NavigationView) findViewById(R.id.nav_view)).getHeaderView(0).findViewById(id); } - // TODO call on current account changed public void restart() { Intent i = new Intent(this, FileDisplayActivity.class); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); startActivity(i); } From 65c8d2449822b2cbe18bdd933c4943b07659a0ab Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Fri, 25 Mar 2016 19:21:39 +0100 Subject: [PATCH 23/84] larger hitbox for the account switcher --- res/layout/drawer_header.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/res/layout/drawer_header.xml b/res/layout/drawer_header.xml index 76471c8aeb0..5adecaac092 100644 --- a/res/layout/drawer_header.xml +++ b/res/layout/drawer_header.xml @@ -18,9 +18,6 @@ Date: Sun, 27 Mar 2016 09:18:39 +0200 Subject: [PATCH 24/84] fix: crash when opening upload dialog --- src/com/owncloud/android/ui/activity/DrawerActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index baf7d366601..aeda0dfe245 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -270,7 +270,7 @@ public void setDrawerIndicatorEnabled(boolean enable) { */ public void updateAccountList() { Account[] accounts = AccountManager.get(this).getAccountsByType(MainApp.getAccountType()); - if(accounts.length > 0) { + if(accounts.length > 0 && mNavigationView != null) { repopulateAccountList(accounts); setUsernameInDrawer(AccountUtils.getCurrentOwnCloudAccount(this)); } From 4e19a57a15e36ed7e213359361c45308fb0af9aa Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 29 Mar 2016 19:02:21 +0200 Subject: [PATCH 25/84] Streamlining moving account handling to a base class b/c it does not belong to toolbar or drawer implementation --- .../android/ui/activity/BaseActivity.java | 224 ++++++++++++++++++ .../android/ui/activity/DrawerActivity.java | 200 +--------------- .../android/ui/activity/ToolbarActivity.java | 3 +- 3 files changed, 232 insertions(+), 195 deletions(-) create mode 100644 src/com/owncloud/android/ui/activity/BaseActivity.java diff --git a/src/com/owncloud/android/ui/activity/BaseActivity.java b/src/com/owncloud/android/ui/activity/BaseActivity.java new file mode 100644 index 00000000000..37a68d184a7 --- /dev/null +++ b/src/com/owncloud/android/ui/activity/BaseActivity.java @@ -0,0 +1,224 @@ +package com.owncloud.android.ui.activity; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.accounts.OperationCanceledException; +import android.os.Bundle; +import android.os.Handler; +import android.support.v7.app.AppCompatActivity; + +import com.owncloud.android.MainApp; +import com.owncloud.android.authentication.AccountUtils; +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.lib.resources.status.OCCapability; + +/** + * Absolute base class holding the oC account. + */ +public abstract class BaseActivity extends AppCompatActivity { + private static final String TAG = BaseActivity.class.getSimpleName(); + + /** + * ownCloud {@link Account} where the main {@link OCFile} handled by the activity is located. + */ + private Account mCurrentAccount; + + /** + * Capabilites of the server where {@link #mCurrentAccount} lives. + */ + private OCCapability mCapabilities; + + /** + * Flag to signal that the activity will is finishing to enforce the creation of an ownCloud {@link Account}. + */ + private boolean mRedirectingToSetupAccount = false; + + /** + * Flag to signal when the value of mAccount was set. + */ + protected boolean mAccountWasSet; + + /** + * Flag to signal when the value of mAccount was restored from a saved state. + */ + protected boolean mAccountWasRestored; + + /** + * Access point to the cached database for the current ownCloud {@link Account}. + */ + private FileDataStorageManager mStorageManager = null; + + /** + * Sets and validates the ownCloud {@link Account} associated to the Activity. + *

+ * If not valid, tries to swap it for other valid and existing ownCloud {@link Account}. + *

+ * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. + * + * @param account New {@link Account} to set. + * @param savedAccount When 'true', account was retrieved from a saved instance state. + */ + protected void setAccount(Account account, boolean savedAccount) { + Account oldAccount = mCurrentAccount; + boolean validAccount = + (account != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), + account.name)); + if (validAccount) { + mCurrentAccount = account; + mAccountWasSet = true; + mAccountWasRestored = (savedAccount || mCurrentAccount.equals(oldAccount)); + + } else { + swapToDefaultAccount(); + } + } + + /** + * Tries to swap the current ownCloud {@link Account} for other valid and existing. + *

+ * If no valid ownCloud {@link Account} exists, the the user is requested + * to create a new ownCloud {@link Account}. + *

+ * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. + */ + protected void swapToDefaultAccount() { + // default to the most recently used account + Account newAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()); + if (newAccount == null) { + /// no account available: force account creation + createAccount(); + mRedirectingToSetupAccount = true; + mAccountWasSet = false; + mAccountWasRestored = false; + + } else { + mAccountWasSet = true; + mAccountWasRestored = (newAccount.equals(mCurrentAccount)); + mCurrentAccount = newAccount; + } + } + + /** + * Launches the account creation activity. To use when no ownCloud account is available. + */ + protected void createAccount() { + AccountManager am = AccountManager.get(getApplicationContext()); + am.addAccount(MainApp.getAccountType(), + null, + null, + null, + this, + new AccountCreationCallback(), + new Handler()); + } + + /** + * Called when the ownCloud {@link Account} associated to the Activity was just updated. + *

+ * Child classes must grant that state depending on the {@link Account} is updated. + */ + protected void onAccountSet(boolean stateWasRecovered) { + if (getAccount() != null) { + mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver()); + mCapabilities = mStorageManager.getCapability(mCurrentAccount.name); + } else { + Log_OC.e(TAG, "onAccountChanged was called with NULL account associated!"); + } + } + + protected void setAccount(Account account) { + mCurrentAccount = account; + } + + /** + * Getter for the capabilities of the server where the current OC account lives. + * + * @return Capabilities of the server where the current OC account lives. Null if the account is not + * set yet. + */ + public OCCapability getCapabilities() { + return mCapabilities; + } + + /** + * Getter for the ownCloud {@link Account} where the main {@link OCFile} handled by the activity + * is located. + * + * @return OwnCloud {@link Account} where the main {@link OCFile} handled by the activity + * is located. + */ + public Account getAccount() { + return mCurrentAccount; + } + + @Override + protected void onStart() { + super.onStart(); + + if (mAccountWasSet) { + onAccountSet(mAccountWasRestored); + } + } + + /** + * @return 'True' when the Activity is finishing to enforce the setup of a new account. + */ + protected boolean isRedirectingToSetupAccount() { + return mRedirectingToSetupAccount; + } + + public FileDataStorageManager getStorageManager() { + return mStorageManager; + } + + /** + * Method that gets called when a new account has been successfully created. + * + * @param future + */ + protected void onAccountCreationSuccessful(AccountManagerFuture future) { + // no special handling in base activity + } + + /** + * Helper class handling a callback from the {@link AccountManager} after the creation of + * a new ownCloud {@link Account} finished, successfully or not. + */ + public class AccountCreationCallback implements AccountManagerCallback { + + @Override + public void run(AccountManagerFuture future) { + BaseActivity.this.mRedirectingToSetupAccount = false; + boolean accountWasSet = false; + if (future != null) { + try { + Bundle result; + result = future.getResult(); + String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); + String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE); + if (AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name)) { + setAccount(new Account(name, type), false); + accountWasSet = true; + } + + onAccountCreationSuccessful(future); + } catch (OperationCanceledException e) { + Log_OC.d(TAG, "Account creation canceled"); + + } catch (Exception e) { + Log_OC.e(TAG, "Account creation finished in exception: ", e); + } + + } else { + Log_OC.e(TAG, "Account creation callback with null bundle"); + } + if (!accountWasSet) { + moveTaskToBack(true); + } + } + } +} diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index aeda0dfe245..5b7e911b790 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -79,41 +79,11 @@ public abstract class DrawerActivity extends ToolbarActivity { */ private ImageView mAccountChooserToggle; - /** - * ownCloud {@link Account} where the main {@link OCFile} handled by the activity is located. - */ - private Account mCurrentAccount; - /** * Flag to signal if the account chooser is active. */ private boolean mIsAccountChooserActive; - /** - * Flag to signal that the activity will is finishing to enforce the creation of an ownCloud {@link Account}. - */ - private boolean mRedirectingToSetupAccount = false; - - /** - * Flag to signal when the value of mAccount was set. - */ - protected boolean mAccountWasSet; - - /** - * Flag to signal when the value of mAccount was restored from a saved state. - */ - protected boolean mAccountWasRestored; - - /** - * Capabilites of the server where {@link #mCurrentAccount} lives. - */ - private OCCapability mCapabilities; - - /** - * Access point to the cached database for the current ownCloud {@link Account}. - */ - private FileDataStorageManager mStorageManager = null; - /** * Initializes the drawer and its content. * This method needs to be called after the content view has been set. @@ -457,7 +427,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { // current account has changed if(data.getBooleanExtra(ManageAccountsActivity.KEY_CURRENT_ACCOUNT_CHANGED, false)) { - mCurrentAccount = AccountUtils.getCurrentOwnCloudAccount(this); + setAccount(AccountUtils.getCurrentOwnCloudAccount(this)); restart(); } else { updateAccountList(); @@ -475,174 +445,16 @@ private View findNavigationViewChildById(int id) { return ((NavigationView) findViewById(R.id.nav_view)).getHeaderView(0).findViewById(id); } - public void restart() { + protected void restart() { Intent i = new Intent(this, FileDisplayActivity.class); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); startActivity(i); } - /** - * Sets and validates the ownCloud {@link Account} associated to the Activity. - *

- * If not valid, tries to swap it for other valid and existing ownCloud {@link Account}. - *

- * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. - * - * @param account New {@link Account} to set. - * @param savedAccount When 'true', account was retrieved from a saved instance state. - */ - protected void setAccount(Account account, boolean savedAccount) { - Account oldAccount = mCurrentAccount; - boolean validAccount = - (account != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), - account.name)); - if (validAccount) { - mCurrentAccount = account; - mAccountWasSet = true; - mAccountWasRestored = (savedAccount || mCurrentAccount.equals(oldAccount)); - - } else { - swapToDefaultAccount(); - } - } - - /** - * Tries to swap the current ownCloud {@link Account} for other valid and existing. - *

- * If no valid ownCloud {@link Account} exists, the the user is requested - * to create a new ownCloud {@link Account}. - *

- * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. - */ - protected void swapToDefaultAccount() { - // default to the most recently used account - Account newAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()); - if (newAccount == null) { - /// no account available: force account creation - createAccount(); - mRedirectingToSetupAccount = true; - mAccountWasSet = false; - mAccountWasRestored = false; - - } else { - mAccountWasSet = true; - mAccountWasRestored = (newAccount.equals(mCurrentAccount)); - mCurrentAccount = newAccount; - } - } - - /** - * Launches the account creation activity. To use when no ownCloud account is available. - */ - private void createAccount() { - AccountManager am = AccountManager.get(getApplicationContext()); - am.addAccount(MainApp.getAccountType(), - null, - null, - null, - this, - new AccountCreationCallback(), - new Handler()); - } - - /** - * Helper class handling a callback from the {@link AccountManager} after the creation of - * a new ownCloud {@link Account} finished, successfully or not. - *

- * At this moment, only called after the creation of the first account. - */ - public class AccountCreationCallback implements AccountManagerCallback { - - @Override - public void run(AccountManagerFuture future) { - DrawerActivity.this.mRedirectingToSetupAccount = false; - boolean accountWasSet = false; - if (future != null) { - try { - Bundle result; - result = future.getResult(); - String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); - String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE); - if (AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name)) { - setAccount(new Account(name, type), false); - accountWasSet = true; - } - - DrawerActivity.this.updateAccountList(); - DrawerActivity.this.restart(); - } catch (OperationCanceledException e) { - Log_OC.d(TAG, "Account creation canceled"); - - } catch (Exception e) { - Log_OC.e(TAG, "Account creation finished in exception: ", e); - } - - } else { - Log_OC.e(TAG, "Account creation callback with null bundle"); - } - if (!accountWasSet) { - moveTaskToBack(true); - } - } - } - - /** - * Called when the ownCloud {@link Account} associated to the Activity was just updated. - *

- * Child classes must grant that state depending on the {@link Account} is updated. - */ - protected void onAccountSet(boolean stateWasRecovered) { - if (getAccount() != null) { - mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver()); - mCapabilities = mStorageManager.getCapability(mCurrentAccount.name); - } else { - Log_OC.e(TAG, "onAccountChanged was called with NULL account associated!"); - } - } - - protected void setAccount(Account account) { - mCurrentAccount = account; - } - - /** - * Getter for the capabilities of the server where the current OC account lives. - * - * @return Capabilities of the server where the current OC account lives. Null if the account is not - * set yet. - */ - public OCCapability getCapabilities() { - return mCapabilities; - } - - /** - * Getter for the ownCloud {@link Account} where the main {@link OCFile} handled by the activity - * is located. - * - * @return OwnCloud {@link Account} where the main {@link OCFile} handled by the activity - * is located. - */ - public Account getAccount() { - return mCurrentAccount; - } - @Override - protected void onStart() { - super.onStart(); - - if (mAccountWasSet) { - onAccountSet(mAccountWasRestored); - } - } - - /** - * @return 'True' when the Activity is finishing to enforce the setup of a new account. - */ - protected boolean isRedirectingToSetupAccount() { - return mRedirectingToSetupAccount; - } - - public FileDataStorageManager getStorageManager() { - return mStorageManager; + protected void onAccountCreationSuccessful(AccountManagerFuture future) { + super.onAccountCreationSuccessful(future); + updateAccountList(); + restart(); } } diff --git a/src/com/owncloud/android/ui/activity/ToolbarActivity.java b/src/com/owncloud/android/ui/activity/ToolbarActivity.java index a41cfeabefa..664ba4827a8 100644 --- a/src/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/src/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -19,6 +19,7 @@ package com.owncloud.android.ui.activity; +import android.accounts.AccountManagerFuture; import android.os.Bundle; import android.support.v4.content.ContextCompat; import android.support.v7.app.ActionBar; @@ -34,7 +35,7 @@ /** * Base class providing toolbar registration functionality, see {@link #setupToolbar()}. */ -public class ToolbarActivity extends AppCompatActivity { +public abstract class ToolbarActivity extends BaseActivity { private ProgressBar mProgressBar; @Override From b482479e8d2ccedf3aaa2e6fc0bc9bc5c9e7baae Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 29 Mar 2016 19:12:10 +0200 Subject: [PATCH 26/84] moved account handling to base activity since this is not file specific and thus should be available on (new) base level --- .../android/ui/activity/BaseActivity.java | 26 ++++++++++++++++ .../android/ui/activity/FileActivity.java | 30 ------------------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/BaseActivity.java b/src/com/owncloud/android/ui/activity/BaseActivity.java index 37a68d184a7..a53e31d919a 100644 --- a/src/com/owncloud/android/ui/activity/BaseActivity.java +++ b/src/com/owncloud/android/ui/activity/BaseActivity.java @@ -5,6 +5,7 @@ import android.accounts.AccountManagerCallback; import android.accounts.AccountManagerFuture; import android.accounts.OperationCanceledException; +import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.support.v7.app.AppCompatActivity; @@ -52,6 +53,31 @@ public abstract class BaseActivity extends AppCompatActivity { */ private FileDataStorageManager mStorageManager = null; + @Override + protected void onNewIntent (Intent intent) { + Log_OC.v(TAG, "onNewIntent() start"); + Account current = AccountUtils.getCurrentOwnCloudAccount(this); + if (current != null && mCurrentAccount != null && !mCurrentAccount.name.equals(current.name)) { + mCurrentAccount = current; + } + Log_OC.v(TAG, "onNewIntent() stop"); + } + + /** + * Since ownCloud {@link Account}s can be managed from the system setting menu, the existence of the {@link + * Account} associated to the instance must be checked every time it is restarted. + */ + @Override + protected void onRestart() { + Log_OC.v(TAG, "onRestart() start"); + super.onRestart(); + boolean validAccount = (mCurrentAccount != null && AccountUtils.exists(mCurrentAccount, this)); + if (!validAccount) { + swapToDefaultAccount(); + } + Log_OC.v(TAG, "onRestart() end"); + } + /** * Sets and validates the ownCloud {@link Account} associated to the Activity. *

diff --git a/src/com/owncloud/android/ui/activity/FileActivity.java b/src/com/owncloud/android/ui/activity/FileActivity.java index 2198a3a4849..522e1436286 100644 --- a/src/com/owncloud/android/ui/activity/FileActivity.java +++ b/src/com/owncloud/android/ui/activity/FileActivity.java @@ -97,9 +97,6 @@ public class FileActivity extends DrawerActivity private static final String DIALOG_UNTRUSTED_CERT = "DIALOG_UNTRUSTED_CERT"; private static final String DIALOG_CERT_NOT_SAVED = "DIALOG_CERT_NOT_SAVED"; - /** OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located.*/ - private Account mAccount; - /** Main {@link OCFile} handled by the activity.*/ private OCFile mFile; @@ -172,33 +169,6 @@ protected void onCreate(Bundle savedInstanceState) { } - @Override - protected void onNewIntent (Intent intent) { - Log_OC.v(TAG, "onNewIntent() start"); - Account current = AccountUtils.getCurrentOwnCloudAccount(this); - if (current != null && mAccount != null && !mAccount.name.equals(current.name)) { - mAccount = current; - } - Log_OC.v(TAG, "onNewIntent() stop"); - } - - /** - * Since ownCloud {@link Account}s can be managed from the system setting menu, - * the existence of the {@link Account} associated to the instance must be checked - * every time it is restarted. - */ - @Override - protected void onRestart() { - Log_OC.v(TAG, "onRestart() start"); - super.onRestart(); - boolean validAccount = (mAccount != null && AccountUtils.exists(mAccount, this)); - if (!validAccount) { - swapToDefaultAccount(); - } - Log_OC.v(TAG, "onRestart() end"); - } - - @Override protected void onStart() { super.onStart(); From 1c574abd05ec19c9f5578e03baab301fdce735ed Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 29 Mar 2016 19:02:21 +0200 Subject: [PATCH 27/84] Streamlining moving account handling to a base class b/c it does not belong to toolbar or drawer implementation --- src/com/owncloud/android/ui/activity/BaseActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/owncloud/android/ui/activity/BaseActivity.java b/src/com/owncloud/android/ui/activity/BaseActivity.java index a53e31d919a..0e66c8797a4 100644 --- a/src/com/owncloud/android/ui/activity/BaseActivity.java +++ b/src/com/owncloud/android/ui/activity/BaseActivity.java @@ -18,7 +18,7 @@ import com.owncloud.android.lib.resources.status.OCCapability; /** - * Absolute base class holding the oC account. + * Base ctivity with common behaviour for activities dealing with ownCloud {@link Account}s . */ public abstract class BaseActivity extends AppCompatActivity { private static final String TAG = BaseActivity.class.getSimpleName(); From 7dad981a368ec52704fc9e59558d606af64c7a18 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 29 Mar 2016 19:19:10 +0200 Subject: [PATCH 28/84] cleanup after drawer-rewrite --- src/com/owncloud/android/ui/activity/FileDisplayActivity.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index 7067e97f00d..9eb9485905b 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -813,9 +813,6 @@ protected void onSaveInstanceState(Bundle outState) { protected void onResume() { Log_OC.v(TAG, "onResume() start"); super.onResume(); - //TODO re-enable account list - // refresh Navigation Drawer account list - //mNavigationDrawerAdapter.updateAccountList(); // refresh list of files refreshListOfFilesFragment(true); From 4d34c470e845788885911aa56bc27d8d03539f74 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 29 Mar 2016 19:49:31 +0200 Subject: [PATCH 29/84] Color of progress bar now aligned with action bar color via color.xml config --- .../owncloud_progressbar_indeterminate_1.png | Bin 462 -> 454 bytes .../owncloud_progressbar_indeterminate_2.png | Bin 574 -> 456 bytes .../owncloud_progressbar_indeterminate_3.png | Bin 673 -> 482 bytes .../owncloud_progressbar_indeterminate_4.png | Bin 688 -> 491 bytes .../owncloud_progressbar_indeterminate_5.png | Bin 682 -> 452 bytes .../owncloud_progressbar_indeterminate_6.png | Bin 683 -> 466 bytes .../owncloud_progressbar_indeterminate_7.png | Bin 645 -> 465 bytes .../owncloud_progressbar_indeterminate_8.png | Bin 631 -> 456 bytes res/drawable/actionbar_progress_horizontal.xml | 9 ++++++--- res/layout/toolbar_standard.xml | 2 +- 10 files changed, 7 insertions(+), 4 deletions(-) diff --git a/res/drawable-xxhdpi/owncloud_progressbar_indeterminate_1.png b/res/drawable-xxhdpi/owncloud_progressbar_indeterminate_1.png index 2444d8361bd6eee706d6c1a73edb95ce2b355c35..c0102e66b414e61e46f099e58927da3e5b666fb9 100644 GIT binary patch literal 454 zcmeAS@N?(olHy`uVBq!ia0y~yV3z{2xj5K>Wb^XqYe0&zILO_JVcj{Imp~3nx}&cn z1H;CC?mvmFKt5-IM`SSr1K(^AW(>XXbrDcdqQo_#Bsf2MUe<&AYDtKf{)}Gu5Yf>-PYI OnZeW5&t;ucLK6T0@T-{s literal 462 zcmeAS@N?(olHy`uVBq!ia0y~yV3z{2xj5K>Wb^XqYe0&n*vT`50|;t3QaXTq&H|6f zVg?4jIUvl)J|*}rP*A4CHKHUqKdq!Zu_%?nF(p4KRlzN@D78GlD7#p}IoRyQo=rUr z42&M0E{-7;bKc%{%sOl!z|;F$jU_wx4{|9=+~yuK@1|*eyM8&BfuX`^CnrNnS{UO3QOP4GcD<`-?*;l82o5CjNil+1 z4Toh+n7}NCc1xgkRi(V$K;6@K*E1i8c%sHIVP+(2Ls*J{^6OpyFYh&(^iF4EZd1o^ z-+*;fe={VcpgDj~%JQ{Tbg_Nb2i3aybLL*FX2eipFSBv(PX31<Wb^XqYe0&zILO_JVcj{Imp~3nx}&cn z1H;CC?mvmFKt5-IM`SSr1GgXuGuEfXPXr1|l(rIsj| z=o#o)<`~!c0S&eEba4!+hxknh?(KRva*$o2cmB+Wy`s!XX{tm@_F)=OLG%{yUcrwo zOM%K53a;syGBD(Uln{`0!ZBVj{P2x(RGKM)@y?GfLW`mUFXHJ*e;JW*YgrI-K gs=;p^zopr0NJ9bhyVZp delta 549 zcmV+=0^0q^1HJ^1B!2{RLP=Bz2nYy#2xN!=000SaNLh0L01FZT01FZU(%pXi0000S zbVXQnQ*UN;cVTj606}DLVr3vkX>w(EZ*psMAVX6&=)AIw0005FNklhxZLDlR z@5ZOVYNQ9{)PLm6r3VTCI8R^Z=H;Rh0GN}W005_g@FLU$pd>#P6l=K7uC?z|nNq%A8_v=%O>}QQ3w&b^lCjf9cLm$M(#sC1O;@Ja$ zQnG{EjD4eV5vy7Z00000006KJE*jaAHhTpCrjciE`+vtV0D!4@_5h%i?4UMnQmf&2 zhuMWb^XqYe0&zILO_JVcj{Imp~3nx}&cn z1H;CC?mvmFKt5-IM`SSr1GgXuGuEfXPXr1|l(rIsj| z=o#o)<`~!c0SyiIba4!+h#*6*;--lma%l1LH zbTuQxhZWJh3=jN(%!1jwu0G%?T|M#aXV<$~4EKY!16|v{>na1oyAU9A`^sqEa{=3> kZra3O8EKj?zJD+Ovsz%E7kB0|U=%QTy85}Sb4q9e0K3J&nE(I) delta 649 zcmV;40(Sl41EB?wB!2{RLP=Bz2nYy#2xN!=000SaNLh0L01FZT01FZU(%pXi0000S zbVXQnQ*UN;cVTj606}DLVr3vkX>w(EZ*psMAVX6&=)AIw0006TNkl`!$G~PzUr-=CWA7TUm z9z(CLupR+`{Zc0Y%zciCAMxDJksG&dY5(qfM7+E3HGdsfJm{1KH%`Ikj@dX}6dte@ zd0rUfIiV#f!GGm*003Hgb%pf^0PL4K0bnjQl~PYfZrrY3|1RH)zuJ3!=ueqRCtP~$ zp&02t4Fe#_Kdj1}q0tBcEK#c~O4cI)uutj)fVo-^r#44!e0;tB-A(r4*E~_JQBYob znFkC|ei5N|yqpIBP*bZbT8k*_5dhd1bppU#`Q?k+uYb+SjoVQ6?@Bk>hqs{*+(WC# z?;QgGD5)6$fEINEKwa;OMz-lL`|wUG00000003YyjYi-e#>Q>$7yv*?%>V$js1pF{ z#%!=3ciD$u^Lx8Pygn(*K=UzN8X&Lu(2`^0HCH;SG3k7)*}G0FX{wCY*e7)Yz+CIX zv2u>w`1pGLyPNF8uW2-%JAn-G@~5Kb^`QY9qR|KdTuZO6upR+`{Zc0Y%$?(Wb^XqYe0&zILO_JVcj{Imp~3nx}&cn z1H;CC?mvmFKt5-IM`SSr1GgXuGuEfXPXr1|l(rIsj| z=o#o)<`~!c0S%4uba4!+h5Pkd zrqdo-E&y`pT+uUSU?>9`#PDGrP*Fkj?kfpyyRSraJ#PSUgLYqGV7LJ?_dqqsw%oTt o&600}k~QZs4^_*>_T874Ss<`)QN^Vfz_?)WboFyt=akR{0P2#ryZ`_I delta 664 zcmaFOyn%IsNrMq>1fPyk5t`Q}{`DrEP ziAAXljw$&`sS0kHMXBZaMcKs)&cS9c_H625U|`bnba4!+nDh3oW7lm15rzx*1)Rk9 zI=$q)#a(jS*Z)qn$S&P4e6dp!?&t{#_fBvxoNQ4)?XO%%W2b~lPTjtL{l@wC>i>K) zpZDI`MP~BF`R*=9-r4Y-{PDHLl-Q>A_GN9K3DIH%m}{rhu;wBUTDN4JHqaWHII zJ1yUWo$-LxNyY}T+P;qYg|8lV#OBVt)%;sf@cp_!xA%W9kA3yu`bcVW?HsdNx6~gS zyuZ}Y>-E@AetCU@wM0|C6+1)1s;GSrWmp(qtFSXfnV2kWv+KKYwJ(ZaZ*95%zOvoF zrbP0X-`_AV)~xNm<_qRx&-)UeXDqqM!Enb}^Zp4-pzXg-0BxW8%WSD-()CYQstU}n z?wVe-(Yy7}pN(frHZ$~3tG}@DOO+izLqb>3KEtvP#e7T+UnkTvGw7bOmkIpEX0b7Q z<|nzBcgv!-KeBMR8F47SnwQ~0R1qh`4K+)K3#)Dk_ilN(Zfl-7GcZDcz+k4EvO}c( zr_JA_7!GWGBE+!4w}@duXsuDk_1NsY-aqHv(Tlsc;HTQr}sbMi^bzz4R0@HurSCW t$BgXbOQ!{Q-wJwtaH;Wb^XqYe0&zILO_JVcj{Imp~3nx}&cn z1H;CC?mvmFKt5-IM`SSr1GgXuGuEfXPXr1|l(rIsj| z=o#o)<`~!c0Sz_vba4!+heA07Lnn(u6^(M^|b`{X@1(z4Gd%k MPgg&ebxsLQ01J?yS^xk5 delta 658 zcmV;D0&V@o1F8j(B!2{RLP=Bz2nYy#2xN!=000SaNLh0L01FZT01FZU(%pXi0000S zbVXQnQ*UN;cVTj606}DLVr3vkX>w(EZ*psMAVX6&=)AIw0006cNkl*6Y3%FcoPVGPL&8q`007MG-ulGe4FK9Dj{~&~1;nAIBc7}$udfaB@v6ueaXV34 zGkoEO&-jqF;cpJpfjvnRavm2I8UcVcl-4IXdp7`RlROU8QmWyU`iLjMoQw-Jt>iP?@y{0K8xFI8f7@i(4#ibWThb00000008jc6dD2cr5|~{_Z|Si zT&z6+;Qf-vftvcH(U#FU+4Aq$c5(Zq%n#Gk)-66dfOVwtu#_D7LO%e2IhEEYWBWII zHvnjhJbw<Ej zcLRVn$>TsRV-JqyeZ-UbmFjEh>y0fJ8c#8OSl-3;pNgIq92dZv6dD15*4$g4*t-Eh syX0}8mQ!5oI^xN?^7`83lZ}4?v0Kj%GTlTD01E&B07*qoM6N<$g0??26aWAK diff --git a/res/drawable-xxhdpi/owncloud_progressbar_indeterminate_6.png b/res/drawable-xxhdpi/owncloud_progressbar_indeterminate_6.png index 815d093b2d10c4b5735118a0337965fdbe516855..d6f4f9475438c62ece5fc410e75b4c0dc4c25290 100644 GIT binary patch literal 466 zcmeAS@N?(olHy`uVBq!ia0y~yV3z{2xj5K>Wb^XqYe0&zILO_JVcj{Imp~3nx}&cn z1H;CC?mvmFKt5-IM`SSr1GgXuGuEfXPXr1|l(rIsj| z=o#o)<`~!c0S$Hbba4!+h?ySD(D3G0A{e4jm~E>7Ir`P!{Z>lZRz~$M=wp-3bg-#B}h17I*`e5258NLy2+Q$zfQ_L5e2mW z!5onA25%sf;Th17g8iN^pOpcHCd`<@2rxsZH0&^X8Y@ a|Nbra9&THFcLgxW89ZJ6T-G@yGywnraJW?f delta 659 zcmV;E0&M-#1FHp)B!2{RLP=Bz2nYy#2xN!=000SaNLh0L01FZT01FZU(%pXi0000S zbVXQnQ*UN;cVTj606}DLVr3vkX>w(EZ*psMAVX6&=)AIw0006dNkl;&5>_GhQrpTTa_E9@S-!EP~%@%AGTaa6^wVSl#U`guSEA{B(Nq)+-% zzx(j%%ir)^ACo)|N`EOya=4V!p#0vTj{sbKP4X+rpCsL-4A<@BrMyq_Imyor`Un8* z*Hodz$@i7}_!`tXvMxz?+_X!t&negBVxRLs z4jl*Ov~Je`Xnz>4%YDv4Ls1S4!!`f_YnqBxoG}4lS@vSshmuI_b54$xd%7 zZnbh`25Y9)MRn1-xCUTbt>B@quiF6ttYzw=!x<9*mVae0hJ7e=xRu*U+H;<#nqz3N z0C>w@~0002Md=iZSyk-5J{9fHjCi z0AO48V%V2@uD9mC^8$rKrE}R_`4Ewc%Pl7C6*vIsm{LTCvK#bEY#U z0Q`UUVt?3&GKX8ao#bhL$30w1-Pu0JbI32mrifQ?ZIOCIBqU tUJU!t=bGOc`>#g#gyZTQS)Zi;2Zj&Lpr9g`*bD#w002ovPDHLkV1iuoFrWYc diff --git a/res/drawable-xxhdpi/owncloud_progressbar_indeterminate_7.png b/res/drawable-xxhdpi/owncloud_progressbar_indeterminate_7.png index c78647de59bc5d4fadd257cf03118c71cf720f31..0a5db16c4ba6da845c3c288b4580e2aed843be49 100644 GIT binary patch literal 465 zcmeAS@N?(olHy`uVBq!ia0y~yV3z{2xj5K>Wb^XqYe0&zILO_JVcj{Imp~3nx}&cn z1H;CC?mvmFKt5-IM`SSr1GgXuGuEfXPXr1|l(rIsj| z=o#o)<`~!c0S$HXba4!+h^v|Cj823lgtzypfnzX)x8n{{Hum_O~vv zGrR#22fTA%GBYg8^p-y$nfo$%R;bj@#k#c&2i5@zhCNrN*)b%{+V+KU&c$hV%OaOf hv>xsnf4=@*$H>#Vol{D++7lS!44$rjF6*2UngDcUv>E^a delta 620 zcmV-y0+apG1BC^UB!2{RLP=Bz2nYy#2xN!=000SaNLh0L01FZT01FZU(%pXi0000S zbVXQnQ*UN;cVTj606}DLVr3vkX>w(EZ*psMAVX6&=)AIw00061NklYqZ$=4)5A23D$fP2>R z8T(}bdL%wVJH91(&bhW@J)X@qufwB9(vf4X#l_%Y_3*i5EG zr|bg&(3-V;#(#bpfF6mD(2l8qIMsKo$6`L7)_U^aOfRx&&Yu=-`kRAb0N$faWR|f- zp%DPsgIYebWWNkRZ^TDvN3EVy>(kSEoZp{%dBQEI9RM$Htt}6P0pKl`A6ThfU-kn4 zXh|)fx%DAk`(*%nAwEJomIeRXH;VPP9_Kl6;0OTwc7JQrv33mrpcQfz0C-J>n~R z(bz2$0ssI20002In?fT1?Av(baqSuaKr7@b0Pvdl2yGdQMxmGQifzG=moLQSL2%iI z%|{2oUX?MWmgtO?egFV1spT`b_RRLn0Q5q9gm#qmo#cLvr`_R9eDNPL8LoN}%4SdV9O&Fde&u)V&q$eir}0000Wb^XqYe0&zILO_JVcj{Imp~3nx}&cn z1H;CC?mvmFKt5-IM`SSr1GgXuGuEfXPXr1|l(rIsj| z=o#o)<`~!c0S&eEba4!+h)&T>JjswcU{kvAz=|nv>sCLX zJza{yAP^|Zuwy02h)kdnW?9pv79XAb9B2f?44@ed40nL486Io_8X>*ayZ7Rwlb?Y` zF!+E(8S;Rt8499yUuo#Qwe(oV$0wB_SEzwR8O%XOSgeiBI^gy;D0xf8r#&E72!li$ zW`m5Fb4|}QVR6|?!zi1|IFQ>qL81qIK}Pri-L?VfGzr~#HtWgo9^3D_`ar3f>OFQx RSbzb|;OXk;vd$@?2>=*5sL%ia delta 606 zcmX@X{GDZjNrMq>1fPyk5t`Q}{`DrEP ziAAXljw$&`sS0kHMXBZaMcKs)&cS9c_H625U|{^{>EaktG3V`F$FAE3B5V)#E4IWf z`oi*-b=TWv>hEl;8XE5{>=x>|?V7a6u!u)aZQ_~w?+29)t~n*@+?yOXr{>3(r@!}a zFrPnpPPfGRi7M^cbGm-~ZLwrJAZ9=5kJl&jlLfs-$7R=lG(34-#eTklJ3~YDwO5kW zWeia&^#{%FPulb3((0R!Z~ZK;`NmTrzWmOt?qoglf|n6TUTzF#ZZOv@zA?kw&}?Sc z<{};jhis76dZ^xD=E7Ik-{ifOxz>JR;mc)n{_Ss%^r=okG)t7B3h2YKWWn8+pMKxFDeJ^*>GC>`=wlx~++;jZmKwKOG(9<0 zhQYx*>+6|I`|i$@Z3sINzrpwBde3*?b9Tj8OZEOXc~bUS?d#!d8ztBo_DX!RI)4=u z4B^0V0P2}zYt68B;{Jr$OWO}UOWqX=(ok~luk$sk`-qF diff --git a/res/drawable/actionbar_progress_horizontal.xml b/res/drawable/actionbar_progress_horizontal.xml index efcf09b41a7..98132694957 100644 --- a/res/drawable/actionbar_progress_horizontal.xml +++ b/res/drawable/actionbar_progress_horizontal.xml @@ -4,15 +4,18 @@ + android:drawable="@color/primary" /> + diff --git a/res/layout/toolbar_standard.xml b/res/layout/toolbar_standard.xml index db7c8c23870..fbe23e5368f 100644 --- a/res/layout/toolbar_standard.xml +++ b/res/layout/toolbar_standard.xml @@ -38,7 +38,7 @@ android:layout_width="match_parent" android:layout_height="4dp" android:layout_margin="0dp" - android:background="@color/background_color" + android:background="@color/primary" android:indeterminate="false" android:indeterminateOnly="false" android:padding="0dp" From fc7f6c05b78c49c769e2f15e5252799080c0d462 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Wed, 30 Mar 2016 15:13:21 +0200 Subject: [PATCH 30/84] layout fix due to added toolbar + oC license added --- res/layout/upload_list_layout.xml | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/res/layout/upload_list_layout.xml b/res/layout/upload_list_layout.xml index 01a476e5bb4..3228d722d56 100755 --- a/res/layout/upload_list_layout.xml +++ b/res/layout/upload_list_layout.xml @@ -1,15 +1,41 @@ + + + android:fitsSystemWindows="true"> + +======= + android:orientation="vertical"> + + +>>>>>>> layout fix due to added toolbar + oC license added Date: Wed, 30 Mar 2016 17:10:40 +0200 Subject: [PATCH 31/84] rebase fixes --- res/layout/upload_list_layout.xml | 7 +--- .../android/ui/activity/DrawerActivity.java | 18 ++++---- .../android/ui/activity/FileActivity.java | 42 +++++++++++++++---- .../ui/activity/FileDisplayActivity.java | 8 ++-- .../android/ui/activity/Preferences.java | 23 ++++------ .../ui/activity/UploadListActivity.java | 16 +++---- 6 files changed, 61 insertions(+), 53 deletions(-) diff --git a/res/layout/upload_list_layout.xml b/res/layout/upload_list_layout.xml index 3228d722d56..6394db210fa 100755 --- a/res/layout/upload_list_layout.xml +++ b/res/layout/upload_list_layout.xml @@ -28,19 +28,16 @@ -======= android:orientation="vertical"> ->>>>>>> layout fix due to added toolbar + oC license added + android:layout_height="match_parent" + android:background="@color/background_color"/> Date: Mon, 4 Apr 2016 10:52:51 +0200 Subject: [PATCH 32/84] removed now unused drawer resource file due to new implementation --- res/values/strings__not_to_translate.xml | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/res/values/strings__not_to_translate.xml b/res/values/strings__not_to_translate.xml index 93f09f0943d..503cc549ec6 100644 --- a/res/values/strings__not_to_translate.xml +++ b/res/values/strings__not_to_translate.xml @@ -29,26 +29,6 @@ MOVE - - - - - @string/drawer_item_all_files - - @string/drawer_item_uploads_list - @string/actionbar_settings - - - - - - - @string/drawer_item_all_files - - @string/drawer_item_uploads_list - @string/drawer_item_settings - - @string/actionbar_sort_by_name From c6eb2546c0f31a0f399e7483f809ffc7ba54b6b4 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Mon, 4 Apr 2016 14:30:11 +0200 Subject: [PATCH 33/84] checked menu item state implementation --- .../android/ui/activity/DrawerActivity.java | 60 ++++++++++++++++--- .../android/ui/activity/FileActivity.java | 2 +- .../ui/activity/FileDisplayActivity.java | 2 +- .../ui/activity/UploadListActivity.java | 2 +- 4 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index d97e81b1e32..87bb833dabb 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -51,6 +51,7 @@ public abstract class DrawerActivity extends ToolbarActivity { private static final String TAG = DrawerActivity.class.getSimpleName(); private static final String KEY_IS_ACCOUNT_CHOOSER_ACTIVE = "IS_ACCOUNT_CHOOSER_ACTIVE"; + private static final String KEY_CHECKED_MENU_ITEM = "CHECKED_MENU_ITEM"; private static final int ACTION_MANAGE_ACCOUNTS = 101; /** @@ -78,6 +79,22 @@ public abstract class DrawerActivity extends ToolbarActivity { */ private boolean mIsAccountChooserActive; + /** + * Id of the checked menu item. + */ + private int mCheckedMenuItem = Menu.NONE; + + /** + * Initializes the drawer, its content and highlights the menu item with the given id. + * This method needs to be called after the content view has been set. + * + * @param menuItemId the menu item to be checked/highlighted + */ + protected void setupDrawer(int menuItemId) { + setupDrawer(); + setDrawerMenuItemChecked(menuItemId); + } + /** * Initializes the drawer and its content. * This method needs to be called after the content view has been set. @@ -142,6 +159,8 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { switch (menuItem.getItemId()) { case R.id.nav_all_files: menuItem.setChecked(true); + mCheckedMenuItem = menuItem.getItemId(); + allFilesOption(); break; case R.id.nav_uploads: @@ -239,7 +258,7 @@ public void setDrawerIndicatorEnabled(boolean enable) { */ public void updateAccountList() { Account[] accounts = AccountManager.get(this).getAccountsByType(MainApp.getAccountType()); - if(accounts.length > 0 && mNavigationView != null) { + if (accounts.length > 0 && mNavigationView != null) { repopulateAccountList(accounts); setUsernameInDrawer(AccountUtils.getCurrentOwnCloudAccount(this)); } @@ -356,12 +375,27 @@ private void showMenu() { } } + /** + * checks/highlights the provided menu item if the drawer has been initialized and the menu item exists. + * + * @param menuItemId the menu item to be highlighted + */ + protected void setDrawerMenuItemChecked(int menuItemId) { + if (mNavigationView.getMenu() != null && mNavigationView.getMenu().findItem(menuItemId) != null) { + mNavigationView.getMenu().findItem(menuItemId).setChecked(true); + mCheckedMenuItem = menuItemId; + } else { + Log_OC.w(TAG, "setDrawerMenuItemChecked has been called with invalid menu-item-ID"); + } + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false); + mCheckedMenuItem = savedInstanceState.getInt(KEY_CHECKED_MENU_ITEM, Menu.NONE); } } @@ -370,6 +404,7 @@ protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, mIsAccountChooserActive); + outState.putInt(KEY_CHECKED_MENU_ITEM, mCheckedMenuItem); } @Override @@ -377,9 +412,15 @@ public void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false); + mCheckedMenuItem = savedInstanceState.getInt(KEY_CHECKED_MENU_ITEM, Menu.NONE); // (re-)setup drawer state showMenu(); + + // check/highlight the menu item if present + if (mCheckedMenuItem > Menu.NONE || mCheckedMenuItem < Menu.NONE) { + setDrawerMenuItemChecked(mCheckedMenuItem); + } } @Override @@ -412,6 +453,12 @@ public void onBackPressed() { super.onBackPressed(); } + @Override + protected void onResume() { + super.onResume(); + setDrawerMenuItemChecked(mCheckedMenuItem); + } + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); @@ -424,7 +471,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { && data.getBooleanExtra(ManageAccountsActivity.KEY_ACCOUNT_LIST_CHANGED, false)) { // current account has changed - if(data.getBooleanExtra(ManageAccountsActivity.KEY_CURRENT_ACCOUNT_CHANGED, false)) { + if (data.getBooleanExtra(ManageAccountsActivity.KEY_CURRENT_ACCOUNT_CHANGED, false)) { setAccount(AccountUtils.getCurrentOwnCloudAccount(this)); restart(); } else { @@ -443,11 +490,10 @@ private View findNavigationViewChildById(int id) { return ((NavigationView) findViewById(R.id.nav_view)).getHeaderView(0).findViewById(id); } - protected void restart() { - Intent i = new Intent(this, FileDisplayActivity.class); - i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(i); - } + /** + * restart helper method which is called after a changing the current account. + */ + protected abstract void restart(); @Override protected void onAccountCreationSuccessful(AccountManagerFuture future) { diff --git a/src/com/owncloud/android/ui/activity/FileActivity.java b/src/com/owncloud/android/ui/activity/FileActivity.java index 5b132c08691..9294e167d94 100644 --- a/src/com/owncloud/android/ui/activity/FileActivity.java +++ b/src/com/owncloud/android/ui/activity/FileActivity.java @@ -530,13 +530,13 @@ public FileUploaderBinder getFileUploaderBinder() { } @Override - public void restart(){ Intent i = new Intent(this, FileDisplayActivity.class); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(i); } + @Override public void allFilesOption(){ restart(); } diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index 88e3b6c1701..5d93ad273a2 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -174,7 +174,7 @@ protected void onCreate(Bundle savedInstanceState) { setupToolbar(); // setup drawer - setupDrawer(); + setupDrawer(R.id.nav_all_files); mDualPane = getResources().getBoolean(R.bool.large_land_layout); mLeftFragmentContainer = findViewById(R.id.left_fragment_container); diff --git a/src/com/owncloud/android/ui/activity/UploadListActivity.java b/src/com/owncloud/android/ui/activity/UploadListActivity.java index 7054fe0ddb7..ec739eb96d7 100755 --- a/src/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/src/com/owncloud/android/ui/activity/UploadListActivity.java @@ -86,7 +86,7 @@ protected void onCreate(Bundle savedInstanceState) { setupToolbar(); // setup drawer - setupDrawer(); + setupDrawer(R.id.nav_uploads); // Add fragment with a transaction for setting a tag if(savedInstanceState == null) { From 601771ddfb7c8cfbd18c60e8a628d52696d2ec7b Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Mon, 4 Apr 2016 18:09:24 +0200 Subject: [PATCH 34/84] standard header layouting, flexible avatar radius, code cleanups, dynamic avatar sizing --- res/layout/drawer_header.xml | 101 ++++++++++-------- res/values-sw360dp/dims.xml | 7 +- res/values/dims.xml | 3 + src/com/owncloud/android/ui/TextDrawable.java | 79 +++++++++++--- .../android/ui/activity/DrawerActivity.java | 65 +++++++---- 5 files changed, 175 insertions(+), 80 deletions(-) diff --git a/res/layout/drawer_header.xml b/res/layout/drawer_header.xml index 5adecaac092..a9cb964cb9d 100644 --- a/res/layout/drawer_header.xml +++ b/res/layout/drawer_header.xml @@ -1,4 +1,5 @@ - - - 164dp + + 164dp + 64dp + + 32px diff --git a/res/values/dims.xml b/res/values/dims.xml index 7e27ae79387..f80b7d55c52 100644 --- a/res/values/dims.xml +++ b/res/values/dims.xml @@ -22,6 +22,9 @@ @dimen/standard_padding 260dp 140dp + 56dp + + 28px 32dp 128dp diff --git a/src/com/owncloud/android/ui/TextDrawable.java b/src/com/owncloud/android/ui/TextDrawable.java index 70dd3521f50..48a6f02ecfd 100644 --- a/src/com/owncloud/android/ui/TextDrawable.java +++ b/src/com/owncloud/android/ui/TextDrawable.java @@ -1,3 +1,23 @@ +/** + * ownCloud Android client application + * + * @author Andy Scherzinger + * @author Tobias Kaminsiky + * Copyright (C) 2016 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package com.owncloud.android.ui; import android.graphics.Canvas; @@ -8,38 +28,73 @@ import android.graphics.drawable.Drawable; /** - * Created by tobi on 24.05.15. + * A Drawable object that draws text (1 letter) on top of a circular/filled background. */ public class TextDrawable extends Drawable { - private final String text; - private final Paint paint; - private final Paint bg; + private String text; + private Paint paint; + private Paint bg; + private float radius; + private float textX; + private float textY; + /** + * Create a TextDrawable with a standard radius. + * + * @param text the text to be rendered + * @param r rgb red value + * @param g rgb green value + * @param b rgb blue value + */ public TextDrawable(String text, int r, int g, int b) { + init(text, r, g, b, 40); + } + + /** + * Create a TextDrawable with the given radius. + * + * @param text the text to be rendered + * @param r rgb red value + * @param g rgb green value + * @param b rgb blue value + * @param radius circle radius + */ + public TextDrawable(String text, int r, int g, int b, float radius) { + init(text, r, g, b, radius); + } + /** + * initializes the TextDrawable. + * + * @param text the text to be rendered + * @param r rgb red value + * @param g rgb green value + * @param b rgb blue value + * @param radius circle radius + */ + private void init(String text, int r, int g, int b, float radius) { + this.radius = radius; this.text = text; - Integer color = Color.rgb(r, g, b); + this.textX = (float) (radius * 0.5); + this.textY = (float) (radius * 1.5); bg = new Paint(); bg.setStyle(Paint.Style.FILL); bg.setAntiAlias(true); - bg.setColor(color); + bg.setColor(Color.rgb(r, g, b)); paint = new Paint(); paint.setColor(Color.WHITE); - paint.setTextSize(60); + paint.setTextSize((float) (radius * 1.5)); paint.setAntiAlias(true); paint.setFakeBoldText(true); } @Override public void draw(Canvas canvas) { - //float[] radii = {radius, radius, radius, radius, radius, radius, radius, radius}; - //new RoundRectShape(radii, null, null); - canvas.drawCircle(40, 40, 40, bg); - //canvas.drawRect(0, -20, 20, 40, bg); - canvas.drawText(text, 20, 60, paint); + canvas.drawCircle(radius, radius, radius, bg); + canvas.drawText(text, textX, textY, paint); } @Override diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index 87bb833dabb..e59d90c9628 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -25,10 +25,12 @@ import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.design.widget.NavigationView; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; +import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -44,6 +46,9 @@ import com.owncloud.android.ui.TextDrawable; import com.owncloud.android.utils.BitmapUtils; +import java.io.UnsupportedEncodingException; +import java.security.NoSuchAlgorithmException; + /** * Base class to handle setup of the drawer implementation. */ @@ -53,6 +58,8 @@ public abstract class DrawerActivity extends ToolbarActivity { private static final String KEY_IS_ACCOUNT_CHOOSER_ACTIVE = "IS_ACCOUNT_CHOOSER_ACTIVE"; private static final String KEY_CHECKED_MENU_ITEM = "CHECKED_MENU_ITEM"; private static final int ACTION_MANAGE_ACCOUNTS = 101; + private static final int MENU_ORDER_ACCOUNT = 1; + private static final int MENU_ORDER_ACCOUNT_FUNCTION = 2; /** * Reference to the drawer layout. @@ -70,7 +77,7 @@ public abstract class DrawerActivity extends ToolbarActivity { private NavigationView mNavigationView; /** - * Reference to the account chooser toogle. + * Reference to the account chooser toggle. */ private ImageView mAccountChooserToggle; @@ -276,21 +283,29 @@ private void repopulateAccountList(Account[] accounts) { // add all accounts to list for (int i = 0; i < accounts.length; i++) { try { - int[] rgb = BitmapUtils.calculateRGB(accounts[i].name); - TextDrawable icon = new TextDrawable(accounts[i].name.substring(0, 1).toUpperCase() - , rgb[0], rgb[1], rgb[2]); - mNavigationView.getMenu().add(R.id.drawer_menu_accounts, Menu.NONE, 1, accounts[i].name).setIcon(icon); + mNavigationView.getMenu().add( + R.id.drawer_menu_accounts, + Menu.NONE, + MENU_ORDER_ACCOUNT, + accounts[i].name) + .setIcon(createAvatar(accounts[i].name, 16)); } catch (Exception e) { Log_OC.e(TAG, "Error calculating RGB value for account menu item.", e); - mNavigationView.getMenu().add(R.id.drawer_menu_accounts, Menu.NONE, 1, accounts[i].name).setIcon(R - .drawable.ic_account_circle); + mNavigationView.getMenu().add( + R.id.drawer_menu_accounts, + Menu.NONE, + MENU_ORDER_ACCOUNT, + accounts[i].name) + .setIcon(R.drawable.ic_account_circle); } } // re-add add-account and manage-accounts - mNavigationView.getMenu().add(R.id.drawer_menu_accounts, R.id.drawer_menu_account_add, 2, + mNavigationView.getMenu().add(R.id.drawer_menu_accounts, R.id.drawer_menu_account_add, + MENU_ORDER_ACCOUNT_FUNCTION, getResources().getString(R.string.prefs_add_account)).setIcon(R.drawable.ic_account_plus); - mNavigationView.getMenu().add(R.id.drawer_menu_accounts, R.id.drawer_menu_account_manage, 2, + mNavigationView.getMenu().add(R.id.drawer_menu_accounts, R.id.drawer_menu_account_manage, + MENU_ORDER_ACCOUNT_FUNCTION, getResources().getString(R.string.drawer_manage_accounts)).setIcon(R.drawable.ic_settings); // adding sets menu group back to visible, so safety check and setting invisible @@ -322,10 +337,8 @@ protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { */ protected void setUsernameInDrawer(Account account) { if (mDrawerLayout != null && account != null) { - TextView username = (TextView) ((NavigationView) findViewById(R.id.nav_view)) - .getHeaderView(0).findViewById(R.id.drawer_username); - TextView usernameFull = (TextView) ((NavigationView) findViewById(R.id.nav_view)) - .getHeaderView(0).findViewById(R.id.drawer_username_full); + TextView username = (TextView) findNavigationViewChildById(R.id.drawer_username); + TextView usernameFull = (TextView) findNavigationViewChildById(R.id.drawer_username_full); usernameFull.setText(account.name); try { OwnCloudAccount oca = new OwnCloudAccount(account, this); @@ -338,13 +351,14 @@ protected void setUsernameInDrawer(Account account) { username.setText(account.name.substring(0, lastAtPos)); } - ImageView userIcon = (ImageView) ((NavigationView) findViewById(R.id.nav_view)) - .getHeaderView(0).findViewById(R.id.drawer_usericon); + ImageView userIcon = (ImageView) findNavigationViewChildById(R.id.drawer_usericon); try { - int[] rgb = BitmapUtils.calculateRGB(account.name); - TextDrawable icon = new TextDrawable( - account.name.substring(0, 1).toUpperCase(), rgb[0], rgb[1], rgb[2]); - userIcon.setImageDrawable(icon); + userIcon.setImageDrawable( + createAvatar( + account.name, + getResources().getDimension(R.dimen.nav_drawer_header_avatar_radius) + ) + ); } catch (Exception e) { Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); userIcon.setImageResource(R.drawable.ic_account_circle); @@ -352,6 +366,19 @@ protected void setUsernameInDrawer(Account account) { } } + @NonNull + private TextDrawable createAvatar(String accountName, float radiusInDp) throws UnsupportedEncodingException, + NoSuchAlgorithmException { + int[] rgb = BitmapUtils.calculateRGB(accountName); + float radiusInPx = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + radiusInDp, + getResources().getDisplayMetrics()); + return new TextDrawable( + accountName.substring(0, 1).toUpperCase(), rgb[0], rgb[1], rgb[2], radiusInPx); + } + + /** * Toggle between standard menu and account list including saving the state. */ From a0811790de924ff1b7fc7e0001de080d96b60a05 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 5 Apr 2016 09:32:39 +0200 Subject: [PATCH 35/84] NPE Fix --- src/com/owncloud/android/ui/activity/DrawerActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index e59d90c9628..2f79b2af003 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -408,7 +408,8 @@ private void showMenu() { * @param menuItemId the menu item to be highlighted */ protected void setDrawerMenuItemChecked(int menuItemId) { - if (mNavigationView.getMenu() != null && mNavigationView.getMenu().findItem(menuItemId) != null) { + if (mNavigationView != null && mNavigationView.getMenu() != null && mNavigationView.getMenu().findItem + (menuItemId) != null) { mNavigationView.getMenu().findItem(menuItemId).setChecked(true); mCheckedMenuItem = menuItemId; } else { From 811c492df7b164c9bd6841e91acc32825a8fb174 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 5 Apr 2016 09:53:21 +0200 Subject: [PATCH 36/84] for null checks --- .../android/ui/activity/DrawerActivity.java | 22 +++++++++++-------- .../ui/activity/UploadFilesActivity.java | 3 +-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index 2f79b2af003..ebb5539c509 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -326,7 +326,9 @@ protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { super.updateActionBarTitleAndHomeButton(chosenFile); /// set home button properties - mDrawerToggle.setDrawerIndicatorEnabled(isRoot(chosenFile)); + if(mDrawerToggle != null) { + mDrawerToggle.setDrawerIndicatorEnabled(isRoot(chosenFile)); + } } /** @@ -391,14 +393,16 @@ private void toggleAccountList() { * depending on the #mIsAccountChooserActive flag shows the account chooser or the standard menu. */ private void showMenu() { - if (mIsAccountChooserActive) { - mAccountChooserToggle.setImageResource(R.drawable.ic_up); - mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, true); - mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, false); - } else { - mAccountChooserToggle.setImageResource(R.drawable.ic_down); - mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false); - mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, true); + if(mNavigationView != null) { + if (mIsAccountChooserActive) { + mAccountChooserToggle.setImageResource(R.drawable.ic_up); + mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, true); + mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, false); + } else { + mAccountChooserToggle.setImageResource(R.drawable.ic_down); + mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false); + mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, true); + } } } diff --git a/src/com/owncloud/android/ui/activity/UploadFilesActivity.java b/src/com/owncloud/android/ui/activity/UploadFilesActivity.java index 566ab795cd2..27dfa41e178 100644 --- a/src/com/owncloud/android/ui/activity/UploadFilesActivity.java +++ b/src/com/owncloud/android/ui/activity/UploadFilesActivity.java @@ -53,9 +53,8 @@ /** * Displays local files and let the user choose what of them wants to upload - * to the current ownCloud account + * to the current ownCloud account. */ - public class UploadFilesActivity extends FileActivity implements LocalFileListFragment.ContainerActivity, ActionBar.OnNavigationListener, OnClickListener, ConfirmationDialogFragmentListener { From 14749c39d03208dac5f6b592584fda0856145f9f Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Sat, 5 Mar 2016 09:49:55 +0100 Subject: [PATCH 37/84] wip Conflicts: res/layout/drawer.xml src/com/owncloud/android/ui/activity/FileActivity.java src/com/owncloud/android/ui/activity/FileDisplayActivity.java --- res/values/dims.xml | 1 + .../datamodel/ThumbnailsCacheManager.java | 260 +++++++++++++++++- .../android/services/OperationsService.java | 1 + .../ui/adapter/FileListListAdapter.java | 12 +- .../ui/adapter/LocalFileListAdapter.java | 20 +- .../ui/fragment/FileDetailFragment.java | 6 +- 6 files changed, 268 insertions(+), 32 deletions(-) diff --git a/res/values/dims.xml b/res/values/dims.xml index f80b7d55c52..178f1142c81 100644 --- a/res/values/dims.xml +++ b/res/values/dims.xml @@ -28,6 +28,7 @@ 32dp 128dp + 128dp 16dp 8dp 16dp diff --git a/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index d5a583eec3a..e75dd61ac9f 100644 --- a/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -21,13 +21,6 @@ package com.owncloud.android.datamodel; -import java.io.File; -import java.io.InputStream; -import java.lang.ref.WeakReference; - -import org.apache.commons.httpclient.HttpStatus; -import org.apache.commons.httpclient.methods.GetMethod; - import android.accounts.Account; import android.content.res.Resources; import android.graphics.Bitmap; @@ -35,7 +28,6 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.media.ThumbnailUtils; import android.net.Uri; @@ -52,7 +44,13 @@ import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.ui.adapter.DiskLruImageCache; import com.owncloud.android.utils.BitmapUtils; -import com.owncloud.android.utils.DisplayUtils; + +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.methods.GetMethod; + +import java.io.File; +import java.io.InputStream; +import java.lang.ref.WeakReference; /** * Manager for concurrent access to thumbnails cache. @@ -361,7 +359,194 @@ private Bitmap doFileInBackground() { } - public static boolean cancelPotentialWork(Object file, ImageView imageView) { + public static class AvatarGenerationTask extends AsyncTask { + private final WeakReference mImageViewReference; + private static Account mAccount; + private Object mUsername; + private FileDataStorageManager mStorageManager; + + + public AvatarGenerationTask(ImageView imageView, FileDataStorageManager storageManager, + Account account) { + // Use a WeakReference to ensure the ImageView can be garbage collected + mImageViewReference = new WeakReference(imageView); + if (storageManager == null) + throw new IllegalArgumentException("storageManager must not be NULL"); + mStorageManager = storageManager; + mAccount = account; + } + + public AvatarGenerationTask(ImageView imageView) { + // Use a WeakReference to ensure the ImageView can be garbage collected + mImageViewReference = new WeakReference(imageView); + } + + @Override + protected Bitmap doInBackground(Object... params) { + Bitmap thumbnail = null; + + try { + if (mAccount != null) { + OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, + MainApp.getAppContext()); + mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). + getClientFor(ocAccount, MainApp.getAppContext()); + } + + mUsername = params[0]; + + if (mUsername instanceof String) { + thumbnail = doAvatarInBackground(); + } + + }catch(Throwable t){ + // the app should never break due to a problem with avatars + Log_OC.e(TAG, "Generation of avatar for " + mUsername + " failed", t); + if (t instanceof OutOfMemoryError) { + System.gc(); + } + } + + return thumbnail; + } + + protected void onPostExecute(Bitmap bitmap) { + if (bitmap != null) { + final ImageView imageView = mImageViewReference.get(); + final AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(imageView); + if (this == avatarWorkerTask) { + String tagId = ""; + if (mUsername instanceof String) { + tagId = (String) mUsername; + if (String.valueOf(imageView.getTag()).equals(tagId)) { + imageView.setImageBitmap(bitmap); + } + } + } + } + } + + /** + * Add thumbnail to cache + * @param imageKey: thumb key + * @param bitmap: image for extracting thumbnail + * @param path: image path + * @param px: thumbnail dp + * @return Bitmap + */ + private Bitmap addThumbnailToCache(String imageKey, Bitmap bitmap, String path, int px){ + + Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px); + + // Rotate image, obeying exif tag + thumbnail = BitmapUtils.rotateImage(thumbnail,path); + + // Add thumbnail to cache + addBitmapToCache(imageKey, thumbnail); + + return thumbnail; + } + + /** + * Converts size of file icon from dp to pixel + * @return int + */ + private int getAvatarDimension(){ + // Converts dp to pixel + Resources r = MainApp.getAppContext().getResources(); + return Math.round(r.getDimension(R.dimen.file_avatar_size)); + } + + private Bitmap doAvatarInBackground() { + String username = (String) mUsername; + + final String imageKey = "a_" + username; + + // Check disk cache in background thread + Bitmap avatar = getBitmapFromDiskCache(imageKey); + + // Not found in disk cache + if (avatar == null) { + + int px = getAvatarDimension(); + + // Download avatar from server + OwnCloudVersion serverOCVersion = AccountUtils.getServerVersion(mAccount); + if (mClient != null && serverOCVersion != null) { + if (serverOCVersion.supportsRemoteThumbnails()) { + GetMethod get = null; + try { + String uri = mClient.getBaseUri() + "" + + "/index.php/avatar/" + username + "/" + px; + Log_OC.d("Avatar", "URI: " + uri); + get = new GetMethod(uri); + int status = mClient.executeMethod(get); + if (status == HttpStatus.SC_OK) { + InputStream inputStream = get.getResponseBodyAsStream(); + Bitmap bitmap = BitmapFactory.decodeStream(inputStream); + avatar = ThumbnailUtils.extractThumbnail(bitmap, px, px); + + // Add avatar to cache + if (avatar != null) { + addBitmapToCache(imageKey, avatar); + } + } else { + mClient.exhaustResponse(get.getResponseBodyAsStream()); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (get != null) { + get.releaseConnection(); + } + } + } else { + Log_OC.d(TAG, "Server too old"); + } + } + } + return avatar; + } + + private Bitmap handlePNG(Bitmap bitmap, int px){ + Bitmap resultBitmap = Bitmap.createBitmap(px, + px, + Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(resultBitmap); + + c.drawColor(MainApp.getAppContext().getResources(). + getColor(R.color.background_color)); + c.drawBitmap(bitmap, 0, 0, null); + + return resultBitmap; + } + + private Bitmap doFileInBackground() { + File file = (File) mUsername; + + final String imageKey = String.valueOf(file.hashCode()); + + // Check disk cache in background thread + Bitmap thumbnail = getBitmapFromDiskCache(imageKey); + + // Not found in disk cache + if (thumbnail == null) { + + int px = getAvatarDimension(); + + Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile( + file.getAbsolutePath(), px, px); + + if (bitmap != null) { + thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px); + } + } + return thumbnail; + } + + } + + public static boolean cancelPotentialThumbnailWork(Object file, ImageView imageView) { final ThumbnailGenerationTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (bitmapWorkerTask != null) { @@ -380,21 +565,51 @@ public static boolean cancelPotentialWork(Object file, ImageView imageView) { return true; } + public static boolean cancelPotentialAvatarWork(Object file, ImageView imageView) { + final AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(imageView); + + if (avatarWorkerTask != null) { + final Object usernameData = avatarWorkerTask.mUsername; + // If usernameData is not yet set or it differs from the new data + if (usernameData == null || usernameData != file) { + // Cancel previous task + avatarWorkerTask.cancel(true); + Log_OC.v(TAG, "Cancelled generation of avatar for a reused imageView"); + } else { + // The same work is already in progress + return false; + } + } + // No task associated with the ImageView, or an existing task was cancelled + return true; + } + public static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { if (imageView != null) { final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncDrawable) { - final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; + if (drawable instanceof AsyncThumbnailDrawable) { + final AsyncThumbnailDrawable asyncDrawable = (AsyncThumbnailDrawable) drawable; return asyncDrawable.getBitmapWorkerTask(); } } return null; } - public static class AsyncDrawable extends BitmapDrawable { + public static AvatarGenerationTask getAvatarWorkerTask(ImageView imageView) { + if (imageView != null) { + final Drawable drawable = imageView.getDrawable(); + if (drawable instanceof AsyncAvatarDrawable) { + final AsyncAvatarDrawable asyncDrawable = (AsyncAvatarDrawable) drawable; + return asyncDrawable.getAvatarWorkerTask(); + } + } + return null; + } + + public static class AsyncThumbnailDrawable extends BitmapDrawable { private final WeakReference bitmapWorkerTaskReference; - public AsyncDrawable( + public AsyncThumbnailDrawable( Resources res, Bitmap bitmap, ThumbnailGenerationTask bitmapWorkerTask ) { @@ -407,4 +622,21 @@ public ThumbnailGenerationTask getBitmapWorkerTask() { return bitmapWorkerTaskReference.get(); } } + + public static class AsyncAvatarDrawable extends BitmapDrawable { + private final WeakReference avatarWorkerTaskReference; + + public AsyncAvatarDrawable( + Resources res, Bitmap bitmap, AvatarGenerationTask avatarWorkerTask + ) { + + super(res, bitmap); + avatarWorkerTaskReference = + new WeakReference(avatarWorkerTask); + } + + public AvatarGenerationTask getAvatarWorkerTask() { + return avatarWorkerTaskReference.get(); + } + } } diff --git a/src/com/owncloud/android/services/OperationsService.java b/src/com/owncloud/android/services/OperationsService.java index da448bdc690..03e404134e1 100644 --- a/src/com/owncloud/android/services/OperationsService.java +++ b/src/com/owncloud/android/services/OperationsService.java @@ -108,6 +108,7 @@ public class OperationsService extends Service { public static final String ACTION_GET_SERVER_INFO = "GET_SERVER_INFO"; public static final String ACTION_OAUTH2_GET_ACCESS_TOKEN = "OAUTH2_GET_ACCESS_TOKEN"; public static final String ACTION_GET_USER_NAME = "GET_USER_NAME"; + public static final String ACTION_GET_USER_AVATAR = "GET_USER_AVATAR"; public static final String ACTION_RENAME = "RENAME"; public static final String ACTION_REMOVE = "REMOVE"; public static final String ACTION_CREATE_FOLDER = "CREATE_FOLDER"; diff --git a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java index 519d85ca7a5..bc5502ae030 100644 --- a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java @@ -306,7 +306,7 @@ public View getView(int position, View convertView, ViewGroup parent) { fileIcon.setImageBitmap(thumbnail); } else { // generate new Thumbnail - if (ThumbnailsCacheManager.cancelPotentialWork(file, fileIcon)) { + if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, fileIcon)) { final ThumbnailsCacheManager.ThumbnailGenerationTask task = new ThumbnailsCacheManager.ThumbnailGenerationTask( fileIcon, mStorageManager, mAccount @@ -314,11 +314,11 @@ public View getView(int position, View convertView, ViewGroup parent) { if (thumbnail == null) { thumbnail = ThumbnailsCacheManager.mDefaultImg; } - final ThumbnailsCacheManager.AsyncDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncDrawable( - mContext.getResources(), - thumbnail, - task + final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncThumbnailDrawable( + mContext.getResources(), + thumbnail, + task ); fileIcon.setImageDrawable(asyncDrawable); task.execute(file); diff --git a/src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java b/src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java index b03b03c1742..7ded153cdfd 100644 --- a/src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java @@ -20,10 +20,7 @@ */ package com.owncloud.android.ui.adapter; -import java.io.File; -import java.io.FileFilter; -import java.util.Arrays; -import java.util.Comparator; +import com.owncloud.android.datamodel.ThumbnailsCacheManager; import android.content.Context; import android.graphics.Bitmap; @@ -43,6 +40,11 @@ import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimetypeIconUtil; +import java.io.File; +import java.io.FileFilter; +import java.util.Arrays; +import java.util.Comparator; + /** * This Adapter populates a ListView with all files and directories contained * in a local directory @@ -112,9 +114,9 @@ public View getView(int position, View convertView, ViewGroup parent) { ImageView fileIcon = (ImageView) view.findViewById(R.id.thumbnail); /** Cancellation needs do be checked and done before changing the drawable in fileIcon, or - * {@link ThumbnailsCacheManager#cancelPotentialWork} will NEVER cancel any task. + * {@link ThumbnailsCacheManager#cancelPotentialThumbnailWork} will NEVER cancel any task. **/ - boolean allowedToCreateNewThumbnail = (ThumbnailsCacheManager.cancelPotentialWork(file, fileIcon)); + boolean allowedToCreateNewThumbnail = (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, fileIcon)); if (!file.isDirectory()) { fileIcon.setImageResource(R.drawable.file); @@ -162,9 +164,9 @@ public View getView(int position, View convertView, ViewGroup parent) { final ThumbnailsCacheManager.ThumbnailGenerationTask task = new ThumbnailsCacheManager.ThumbnailGenerationTask(fileIcon); thumbnail = ThumbnailsCacheManager.mDefaultImg; - final ThumbnailsCacheManager.AsyncDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncDrawable( - mContext.getResources(), + final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncThumbnailDrawable( + mContext.getResources(), thumbnail, task ); diff --git a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java index ffd20c2c1cd..17783ff46f4 100644 --- a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -443,7 +443,7 @@ private void setFiletype(OCFile file) { iv.setImageBitmap(thumbnail); } else { // generate new Thumbnail - if (ThumbnailsCacheManager.cancelPotentialWork(file, iv)) { + if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, iv)) { final ThumbnailsCacheManager.ThumbnailGenerationTask task = new ThumbnailsCacheManager.ThumbnailGenerationTask( iv, mContainerActivity.getStorageManager(), mAccount @@ -451,8 +451,8 @@ private void setFiletype(OCFile file) { if (thumbnail == null) { thumbnail = ThumbnailsCacheManager.mDefaultImg; } - final ThumbnailsCacheManager.AsyncDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncDrawable( + final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncThumbnailDrawable( MainApp.getAppContext().getResources(), thumbnail, task From e599b7b8f282afffb91d4441c01ccc057ce8ca2b Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 5 Apr 2016 10:57:30 +0200 Subject: [PATCH 38/84] merged avatar branch + circular avatar in drawer header --- .../datamodel/ThumbnailsCacheManager.java | 6 +- .../android/ui/activity/DrawerActivity.java | 74 ++++++++++++++++--- .../ui/activity/FileDisplayActivity.java | 4 +- .../ui/activity/UploadListActivity.java | 2 +- .../adapter/ExpandableUploadListAdapter.java | 10 +-- .../adapter/ReceiveExternalFilesAdapter.java | 6 +- 6 files changed, 77 insertions(+), 25 deletions(-) diff --git a/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index e75dd61ac9f..410bf223866 100644 --- a/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -32,6 +32,8 @@ import android.media.ThumbnailUtils; import android.net.Uri; import android.os.AsyncTask; +import android.support.v4.graphics.drawable.RoundedBitmapDrawable; +import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; import android.widget.ImageView; import com.owncloud.android.MainApp; @@ -399,7 +401,7 @@ protected Bitmap doInBackground(Object... params) { thumbnail = doAvatarInBackground(); } - }catch(Throwable t){ + } catch(Throwable t){ // the app should never break due to a problem with avatars Log_OC.e(TAG, "Generation of avatar for " + mUsername + " failed", t); if (t instanceof OutOfMemoryError) { @@ -494,7 +496,7 @@ private Bitmap doAvatarInBackground() { mClient.exhaustResponse(get.getResponseBodyAsStream()); } } catch (Exception e) { - e.printStackTrace(); + Log_OC.e(TAG, "Error downloading avatar", e); } finally { if (get != null) { get.releaseConnection(); diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index ebb5539c509..aca1b4846d5 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -24,9 +24,12 @@ import android.accounts.AccountManagerFuture; import android.content.Intent; import android.content.res.Configuration; +import android.graphics.Bitmap; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.NavigationView; +import android.support.v4.graphics.drawable.RoundedBitmapDrawable; +import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; @@ -41,6 +44,7 @@ import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.TextDrawable; @@ -267,7 +271,7 @@ public void updateAccountList() { Account[] accounts = AccountManager.get(this).getAccountsByType(MainApp.getAccountType()); if (accounts.length > 0 && mNavigationView != null) { repopulateAccountList(accounts); - setUsernameInDrawer(AccountUtils.getCurrentOwnCloudAccount(this)); + setAccountInDrawer(AccountUtils.getCurrentOwnCloudAccount(this)); } } @@ -337,7 +341,7 @@ protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { * * @param account the account to be set in the drawer */ - protected void setUsernameInDrawer(Account account) { + protected void setAccountInDrawer(Account account) { if (mDrawerLayout != null && account != null) { TextView username = (TextView) findNavigationViewChildById(R.id.drawer_username); TextView usernameFull = (TextView) findNavigationViewChildById(R.id.drawer_username_full); @@ -354,16 +358,62 @@ protected void setUsernameInDrawer(Account account) { } ImageView userIcon = (ImageView) findNavigationViewChildById(R.id.drawer_usericon); - try { - userIcon.setImageDrawable( - createAvatar( - account.name, - getResources().getDimension(R.dimen.nav_drawer_header_avatar_radius) - ) - ); - } catch (Exception e) { - Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); - userIcon.setImageResource(R.drawable.ic_account_circle); + setAvatarInDrawer(account); + } + } + + /** + * sets the avatar of the current account in the drawer in case the drawer is available. + * + * @param account the account to be set in the drawer + */ + private void setAvatarInDrawer(Account account) { + if (mDrawerLayout != null && account != null) { + ImageView userIcon = (ImageView) findNavigationViewChildById(R.id.drawer_usericon); + int lastAtPos = account.name.lastIndexOf("@"); + String username = account.name.substring(0, lastAtPos); + + // Thumbnail in Cache? + Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + username); + + if (thumbnail != null) { + RoundedBitmapDrawable roundedAvatar = RoundedBitmapDrawableFactory.create + (MainApp.getAppContext().getResources(), thumbnail); + roundedAvatar.setCircular(true); + userIcon.setImageDrawable(roundedAvatar); + } else { + // generate new avatar + if (ThumbnailsCacheManager.cancelPotentialAvatarWork(username, userIcon)) { + final ThumbnailsCacheManager.AvatarGenerationTask task = + new ThumbnailsCacheManager.AvatarGenerationTask( + userIcon, getStorageManager(), account + ); + if (thumbnail == null) { + try { + userIcon.setImageDrawable( + createAvatar( + account.name, + getResources().getDimension(R.dimen.nav_drawer_header_avatar_radius) + ) + ); + } catch (Exception e) { + Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); + userIcon.setImageResource(R.drawable.ic_account_circle); + } + } else { + final ThumbnailsCacheManager.AsyncAvatarDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncAvatarDrawable( + getResources(), + thumbnail, + task + ); + RoundedBitmapDrawable roundedAvatar = RoundedBitmapDrawableFactory.create + (MainApp.getAppContext().getResources(), asyncDrawable.getBitmap()); + roundedAvatar.setCircular(true); + userIcon.setImageDrawable(roundedAvatar); + } + task.execute(username); + } } } } diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index 5d93ad273a2..4e55afaefab 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -305,7 +305,7 @@ protected void onAccountSet(boolean stateWasRecovered) { setFile(file); if (mAccountWasSet) { - setUsernameInDrawer(getAccount()); + setAccountInDrawer(getAccount()); } if (!stateWasRecovered) { @@ -968,7 +968,7 @@ public void onReceive(Context context, Intent intent) { } if (synchFolderRemotePath.equals(OCFile.ROOT_PATH)) { - setUsernameInDrawer(getAccount()); + setAccountInDrawer(getAccount()); } } diff --git a/src/com/owncloud/android/ui/activity/UploadListActivity.java b/src/com/owncloud/android/ui/activity/UploadListActivity.java index ec739eb96d7..57aad23987b 100755 --- a/src/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/src/com/owncloud/android/ui/activity/UploadListActivity.java @@ -350,7 +350,7 @@ protected void onAccountSet(boolean stateWasRecovered) { super.onAccountSet(stateWasRecovered); getSupportActionBar().setTitle(getString(R.string.uploads_view_title)); if (mAccountWasSet) { - setUsernameInDrawer(getAccount()); + setAccountInDrawer(getAccount()); } } diff --git a/src/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java b/src/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java index dd08e135227..332ed82b3ba 100755 --- a/src/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java @@ -409,7 +409,7 @@ public void onClick(View v) { fakeFileToCheatThumbnailsCacheManagerInterface.setStoragePath(upload.getLocalPath()); fakeFileToCheatThumbnailsCacheManagerInterface.setMimetype(upload.getMimeType()); - boolean allowedToCreateNewThumbnail = (ThumbnailsCacheManager.cancelPotentialWork( + boolean allowedToCreateNewThumbnail = (ThumbnailsCacheManager.cancelPotentialThumbnailWork( fakeFileToCheatThumbnailsCacheManagerInterface, fileIcon) ); @@ -434,8 +434,8 @@ public void onClick(View v) { if (thumbnail == null) { thumbnail = ThumbnailsCacheManager.mDefaultImg; } - final ThumbnailsCacheManager.AsyncDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncDrawable( + final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncThumbnailDrawable( mParentActivity.getResources(), thumbnail, task @@ -466,8 +466,8 @@ public void onClick(View v) { if (thumbnail == null) { thumbnail = ThumbnailsCacheManager.mDefaultImg; } - final ThumbnailsCacheManager.AsyncDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncDrawable( + final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncThumbnailDrawable( mParentActivity.getResources(), thumbnail, task diff --git a/src/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.java b/src/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.java index 1e087ae36be..f92afb5c51a 100644 --- a/src/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.java +++ b/src/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.java @@ -35,7 +35,7 @@ import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.ThumbnailsCacheManager; -import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncDrawable; +import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncThumbnailDrawable; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimetypeIconUtil; @@ -120,14 +120,14 @@ public View getView(int position, View convertView, ViewGroup parent) { fileIcon.setImageBitmap(thumbnail); } else { // generate new Thumbnail - if (ThumbnailsCacheManager.cancelPotentialWork(file, fileIcon)) { + if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, fileIcon)) { final ThumbnailsCacheManager.ThumbnailGenerationTask task = new ThumbnailsCacheManager.ThumbnailGenerationTask(fileIcon, mStorageManager, mAccount); if (thumbnail == null) { thumbnail = ThumbnailsCacheManager.mDefaultImg; } - final AsyncDrawable asyncDrawable = new AsyncDrawable( + final AsyncThumbnailDrawable asyncDrawable = new AsyncThumbnailDrawable( mContext.getResources(), thumbnail, task From 812e889b624e43151532fcd27afa0450372ef5c8 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 5 Apr 2016 11:06:00 +0200 Subject: [PATCH 39/84] JavaDoc --- src/com/owncloud/android/ui/activity/DrawerActivity.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index aca1b4846d5..a0578375103 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -363,7 +363,7 @@ protected void setAccountInDrawer(Account account) { } /** - * sets the avatar of the current account in the drawer in case the drawer is available. + * fetches and sets the avatar of the current account in the drawer in case the drawer is available. * * @param account the account to be set in the drawer */ @@ -430,7 +430,6 @@ private TextDrawable createAvatar(String accountName, float radiusInDp) throws U accountName.substring(0, 1).toUpperCase(), rgb[0], rgb[1], rgb[2], radiusInPx); } - /** * Toggle between standard menu and account list including saving the state. */ From aea547143c37785d71deaced8a3184bdc7f22d6d Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 5 Apr 2016 16:28:56 +0200 Subject: [PATCH 40/84] rudimentary drawer header / avatar based account switching. --- res/layout/drawer_header.xml | 42 +++++-- res/values/dims.xml | 3 + .../android/ui/activity/DrawerActivity.java | 118 ++++++++++++++++-- 3 files changed, 145 insertions(+), 18 deletions(-) diff --git a/res/layout/drawer_header.xml b/res/layout/drawer_header.xml index a9cb964cb9d..1b573d63335 100644 --- a/res/layout/drawer_header.xml +++ b/res/layout/drawer_header.xml @@ -29,19 +29,43 @@ android:padding="@dimen/standard_padding" > - + + + + + + + + + 56dp 28px + 40dp + + 56dp 32dp 128dp diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index a0578375103..4436802e98d 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -22,10 +22,13 @@ import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerFuture; +import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.res.Configuration; import android.graphics.Bitmap; import android.os.Bundle; +import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.design.widget.NavigationView; import android.support.v4.graphics.drawable.RoundedBitmapDrawable; @@ -54,7 +57,8 @@ import java.security.NoSuchAlgorithmException; /** - * Base class to handle setup of the drawer implementation. + * Base class to handle setup of the drawer implementation including user switching and avatar fetching and fallback + * generation. */ public abstract class DrawerActivity extends ToolbarActivity { @@ -85,6 +89,21 @@ public abstract class DrawerActivity extends ToolbarActivity { */ private ImageView mAccountChooserToggle; + /** + * Reference to the current account avatar. + */ + private ImageView mAccountCurrentAccountAvatar; + + /** + * Reference to the middle account avatar. + */ + private ImageView mAccountMiddleAccountAvatar; + + /** + * Reference to the end account avatar. + */ + private ImageView mAccountEndAccountAvatar; + /** * Flag to signal if the account chooser is active. */ @@ -95,6 +114,11 @@ public abstract class DrawerActivity extends ToolbarActivity { */ private int mCheckedMenuItem = Menu.NONE; + /** + * accounts for the (max) three displayed accounts in the drawer header. + */ + private Account[] mAvatars = new Account[3]; + /** * Initializes the drawer, its content and highlights the menu item with the given id. * This method needs to be called after the content view has been set. @@ -115,11 +139,16 @@ protected void setupDrawer() { mNavigationView = (NavigationView) findViewById(R.id.nav_view); if (mNavigationView != null) { - setupDrawerContent(mNavigationView); mAccountChooserToggle = (ImageView) findNavigationViewChildById(R.id.drawer_account_chooser_toogle); mAccountChooserToggle.setImageResource(R.drawable.ic_down); mIsAccountChooserActive = false; + mAccountCurrentAccountAvatar = (ImageView) findNavigationViewChildById(R.id.drawer_current_account); + mAccountMiddleAccountAvatar = (ImageView) findNavigationViewChildById(R.id.drawer_account_middle); + mAccountEndAccountAvatar = (ImageView) findNavigationViewChildById(R.id.drawer_account_end); + + setupDrawerContent(mNavigationView); + findNavigationViewChildById(R.id.drawer_active_user) .setOnClickListener(new View.OnClickListener() { @Override @@ -195,9 +224,7 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { break; case Menu.NONE: // account clicked - AccountUtils.setCurrentOwnCloudAccount( - getApplicationContext(), menuItem.getTitle().toString()); - restart(); + accountClicked(menuItem.getTitle().toString()); default: Log_OC.i(TAG, "Unknown drawer menu item clicked: " + menuItem.getTitle()); } @@ -214,6 +241,28 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { } } + /** + * sets the new/current account and restarts. In case the given account equals the actual/current account the + * call will be ignored. + * + * @param accountName The account name to be set + */ + private void accountClicked(String accountName) { + if (!AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()).name.equals(accountName)) { + AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), accountName); + restart(); + } + } + + /** + * click method for mini avatars in drawer header. + * + * @param view the clicked ImageView + */ + public void onAccountDrawerClick(View view) { + accountClicked(view.getContentDescription().toString()); + } + /** * checks if the drawer exists and is opened. * @@ -272,6 +321,26 @@ public void updateAccountList() { if (accounts.length > 0 && mNavigationView != null) { repopulateAccountList(accounts); setAccountInDrawer(AccountUtils.getCurrentOwnCloudAccount(this)); + populateDrawerOwnCloudAccounts(); + + // activate second/end account avatar + if(mAvatars[1] != null) { + setAvatar(mAvatars[1], R.id.drawer_account_end); + mAccountEndAccountAvatar.setVisibility(View.VISIBLE); + } else { + mAccountEndAccountAvatar.setVisibility(View.GONE); + } + + // activate third/middle account avatar + if(mAvatars[2] != null) { + setAvatar(mAvatars[2], R.id.drawer_account_middle); + mAccountMiddleAccountAvatar.setVisibility(View.VISIBLE); + } else { + mAccountMiddleAccountAvatar.setVisibility(View.GONE); + } + } else { + mAccountEndAccountAvatar.setVisibility(View.GONE); + mAccountMiddleAccountAvatar.setVisibility(View.GONE); } } @@ -357,8 +426,7 @@ protected void setAccountInDrawer(Account account) { username.setText(account.name.substring(0, lastAtPos)); } - ImageView userIcon = (ImageView) findNavigationViewChildById(R.id.drawer_usericon); - setAvatarInDrawer(account); + setAvatar(account, R.id.drawer_current_account); } } @@ -367,12 +435,14 @@ protected void setAccountInDrawer(Account account) { * * @param account the account to be set in the drawer */ - private void setAvatarInDrawer(Account account) { + private void setAvatar(Account account, int avatarViewId) { if (mDrawerLayout != null && account != null) { - ImageView userIcon = (ImageView) findNavigationViewChildById(R.id.drawer_usericon); int lastAtPos = account.name.lastIndexOf("@"); String username = account.name.substring(0, lastAtPos); + ImageView userIcon = (ImageView) findNavigationViewChildById(avatarViewId); + userIcon.setContentDescription(account.name); + // Thumbnail in Cache? Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + username); @@ -418,6 +488,16 @@ userIcon, getStorageManager(), account } } + /** + * creates an avatar in form of a TextDrawable with the first letter of the account name in a circle with the + * given radius. + * + * @param accountName the account name + * @param radiusInDp the circle's radius + * @return the avatar as a TextDrawable + * @throws UnsupportedEncodingException if the charset is not supported when calculating the color values + * @throws NoSuchAlgorithmException if the specified algorithm is not available when calculating the color values + */ @NonNull private TextDrawable createAvatar(String accountName, float radiusInDp) throws UnsupportedEncodingException, NoSuchAlgorithmException { @@ -582,4 +662,24 @@ protected void onAccountCreationSuccessful(AccountManagerFuture future) updateAccountList(); restart(); } + + /** + * populates the avatar drawer array with the first three ownCloud {@link Account}s while the first element is + * always the current account. + */ + private void populateDrawerOwnCloudAccounts() { + mAvatars = new Account[3]; + Account[] accountsAll = AccountManager.get(this).getAccountsByType + (MainApp.getAccountType()); + Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(this); + + mAvatars[0] = currentAccount; + int j = 0; + for(int i = 1 ; i <= 2 && i < accountsAll.length && j < accountsAll.length; j++) { + if(!currentAccount.equals(accountsAll[j])) { + mAvatars[i] = accountsAll[j]; + i++; + } + } + } } From 99516540e40acd8ce7450fefdf4d81f693f50ca6 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 5 Apr 2016 20:31:19 +0200 Subject: [PATCH 41/84] Fix for dynamic avatar sizing for generic letter/circle avatars --- res/values/dims.xml | 1 + .../android/ui/activity/DrawerActivity.java | 40 ++++++++++++++----- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/res/values/dims.xml b/res/values/dims.xml index b114a0fa9ac..fd37d96bbb4 100644 --- a/res/values/dims.xml +++ b/res/values/dims.xml @@ -26,6 +26,7 @@ 28px 40dp + 20px 56dp diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index 4436802e98d..eb8164386eb 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -69,6 +69,16 @@ public abstract class DrawerActivity extends ToolbarActivity { private static final int MENU_ORDER_ACCOUNT = 1; private static final int MENU_ORDER_ACCOUNT_FUNCTION = 2; + /** + * current account avatar radius. + */ + private float mCurrentAccountAvatarRadiusDimension; + + /** + * other accounts avatar radius. + */ + private float mOtherAccountAvatarRadiusDimension; + /** * Reference to the drawer layout. */ @@ -325,7 +335,7 @@ public void updateAccountList() { // activate second/end account avatar if(mAvatars[1] != null) { - setAvatar(mAvatars[1], R.id.drawer_account_end); + setAvatar(mAvatars[1], R.id.drawer_account_end, false); mAccountEndAccountAvatar.setVisibility(View.VISIBLE); } else { mAccountEndAccountAvatar.setVisibility(View.GONE); @@ -333,7 +343,7 @@ public void updateAccountList() { // activate third/middle account avatar if(mAvatars[2] != null) { - setAvatar(mAvatars[2], R.id.drawer_account_middle); + setAvatar(mAvatars[2], R.id.drawer_account_middle, false); mAccountMiddleAccountAvatar.setVisibility(View.VISIBLE); } else { mAccountMiddleAccountAvatar.setVisibility(View.GONE); @@ -426,7 +436,7 @@ protected void setAccountInDrawer(Account account) { username.setText(account.name.substring(0, lastAtPos)); } - setAvatar(account, R.id.drawer_current_account); + setAvatar(account, R.id.drawer_current_account, true); } } @@ -434,8 +444,10 @@ protected void setAccountInDrawer(Account account) { * fetches and sets the avatar of the current account in the drawer in case the drawer is available. * * @param account the account to be set in the drawer + * @param avatarViewId the view to set the avatar on + * @param currentAccount flag if it is the current avatar or another (impacts chosen size) */ - private void setAvatar(Account account, int avatarViewId) { + private void setAvatar(Account account, int avatarViewId, boolean currentAccount) { if (mDrawerLayout != null && account != null) { int lastAtPos = account.name.lastIndexOf("@"); String username = account.name.substring(0, lastAtPos); @@ -460,12 +472,15 @@ userIcon, getStorageManager(), account ); if (thumbnail == null) { try { - userIcon.setImageDrawable( - createAvatar( - account.name, - getResources().getDimension(R.dimen.nav_drawer_header_avatar_radius) - ) - ); + if (currentAccount) { + userIcon.setImageDrawable( + createAvatar(account.name, mCurrentAccountAvatarRadiusDimension) + ); + } else { + userIcon.setImageDrawable( + createAvatar(account.name, mOtherAccountAvatarRadiusDimension) + ); + } } catch (Exception e) { Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); userIcon.setImageResource(R.drawable.ic_account_circle); @@ -558,6 +573,11 @@ protected void onCreate(Bundle savedInstanceState) { mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false); mCheckedMenuItem = savedInstanceState.getInt(KEY_CHECKED_MENU_ITEM, Menu.NONE); } + + mCurrentAccountAvatarRadiusDimension = getResources() + .getDimension(R.dimen.nav_drawer_header_avatar_radius); + mOtherAccountAvatarRadiusDimension = getResources() + .getDimension(R.dimen.nav_drawer_header_avatar_other_accounts_radius); } @Override From b7b80fc2a59eba01f7941d485e1308d102dba243 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 5 Apr 2016 22:26:49 +0200 Subject: [PATCH 42/84] color fix for EditText (due to wrong parent style) --- res/values/styles.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/values/styles.xml b/res/values/styles.xml index 8be29c5354f..1d424e9bdcd 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -36,7 +36,7 @@ - - + + 56dp - 16dp + 14sp 20dp 32dp From 177f01e1d62b5769bc8c4541efcb23f74a7ecf09 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Wed, 6 Apr 2016 23:36:01 +0200 Subject: [PATCH 49/84] drawer preparations for "On Device" menu item --- res/menu/drawer_menu.xml | 7 +++++++ .../owncloud/android/ui/activity/DrawerActivity.java | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/res/menu/drawer_menu.xml b/res/menu/drawer_menu.xml index 8594e6390f8..ef8e4f8335f 100644 --- a/res/menu/drawer_menu.xml +++ b/res/menu/drawer_menu.xml @@ -27,6 +27,13 @@ android:id="@+id/nav_all_files" android:icon="@drawable/ic_folder_open" android:title="@string/drawer_item_all_files"/> + Date: Thu, 7 Apr 2016 10:32:06 +0200 Subject: [PATCH 50/84] extend implementation to also set avatars in menu items --- .../datamodel/ThumbnailsCacheManager.java | 86 ++++++++++++++++--- .../android/ui/activity/DrawerActivity.java | 59 ++++++++++++- 2 files changed, 132 insertions(+), 13 deletions(-) diff --git a/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index 410bf223866..33c44061825 100644 --- a/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -34,6 +34,7 @@ import android.os.AsyncTask; import android.support.v4.graphics.drawable.RoundedBitmapDrawable; import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; +import android.view.MenuItem; import android.widget.ImageView; import com.owncloud.android.MainApp; @@ -363,6 +364,7 @@ private Bitmap doFileInBackground() { public static class AvatarGenerationTask extends AsyncTask { private final WeakReference mImageViewReference; + private final WeakReference mMenuItemReference; private static Account mAccount; private Object mUsername; private FileDataStorageManager mStorageManager; @@ -371,6 +373,7 @@ public static class AvatarGenerationTask extends AsyncTask public AvatarGenerationTask(ImageView imageView, FileDataStorageManager storageManager, Account account) { // Use a WeakReference to ensure the ImageView can be garbage collected + mMenuItemReference = null; mImageViewReference = new WeakReference(imageView); if (storageManager == null) throw new IllegalArgumentException("storageManager must not be NULL"); @@ -378,8 +381,20 @@ public AvatarGenerationTask(ImageView imageView, FileDataStorageManager storageM mAccount = account; } + public AvatarGenerationTask(MenuItem menuItem, FileDataStorageManager storageManager, + Account account) { + // Use a WeakReference to ensure the ImageView can be garbage collected + mImageViewReference = null; + mMenuItemReference = new WeakReference(menuItem); + if (storageManager == null) + throw new IllegalArgumentException("storageManager must not be NULL"); + mStorageManager = storageManager; + mAccount = account; + } + public AvatarGenerationTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can be garbage collected + mMenuItemReference = null; mImageViewReference = new WeakReference(imageView); } @@ -414,14 +429,29 @@ protected Bitmap doInBackground(Object... params) { protected void onPostExecute(Bitmap bitmap) { if (bitmap != null) { - final ImageView imageView = mImageViewReference.get(); - final AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(imageView); - if (this == avatarWorkerTask) { - String tagId = ""; - if (mUsername instanceof String) { - tagId = (String) mUsername; - if (String.valueOf(imageView.getTag()).equals(tagId)) { - imageView.setImageBitmap(bitmap); + if (mImageViewReference != null) { + ImageView imageView = mImageViewReference.get(); + AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(imageView); + if (this == avatarWorkerTask) { + String tagId = ""; + if (mUsername instanceof String) { + tagId = (String) mUsername; + if (String.valueOf(imageView.getTag()).equals(tagId)) { + imageView.setImageBitmap(bitmap); + } + } + } + } else { + MenuItem menuItem = mMenuItemReference.get(); + AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(menuItem); + if (this == avatarWorkerTask) { + String tagId = ""; + if (mUsername instanceof String) { + tagId = (String) mUsername; + if (String.valueOf(menuItem.getTitle()).equals(tagId)) { + menuItem.setIcon(new BitmapDrawable(MainApp.getAppContext().getResources(), + bitmap)); + } } } } @@ -586,6 +616,25 @@ public static boolean cancelPotentialAvatarWork(Object file, ImageView imageView return true; } + public static boolean cancelPotentialAvatarWork(Object file, MenuItem menuItem) { + final AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(menuItem); + + if (avatarWorkerTask != null) { + final Object usernameData = avatarWorkerTask.mUsername; + // If usernameData is not yet set or it differs from the new data + if (usernameData == null || usernameData != file) { + // Cancel previous task + avatarWorkerTask.cancel(true); + Log_OC.v(TAG, "Cancelled generation of avatar for a reused imageView"); + } else { + // The same work is already in progress + return false; + } + } + // No task associated with the ImageView, or an existing task was cancelled + return true; + } + public static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { if (imageView != null) { final Drawable drawable = imageView.getDrawable(); @@ -599,15 +648,28 @@ public static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { public static AvatarGenerationTask getAvatarWorkerTask(ImageView imageView) { if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncAvatarDrawable) { - final AsyncAvatarDrawable asyncDrawable = (AsyncAvatarDrawable) drawable; - return asyncDrawable.getAvatarWorkerTask(); + if (imageView != null) { + return getAvatarWorkerTask(imageView.getDrawable()); } } return null; } + public static AvatarGenerationTask getAvatarWorkerTask(MenuItem menuItem) { + if (menuItem != null) { + return getAvatarWorkerTask(menuItem.getIcon()); + } + return null; + } + + public static AvatarGenerationTask getAvatarWorkerTask(Drawable drawable) { + if (drawable instanceof AsyncAvatarDrawable) { + final AsyncAvatarDrawable asyncDrawable = (AsyncAvatarDrawable) drawable; + return asyncDrawable.getAvatarWorkerTask(); + } + return null; + } + public static class AsyncThumbnailDrawable extends BitmapDrawable { private final WeakReference bitmapWorkerTaskReference; diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index 5fe784cdefb..c47e55466e6 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -373,7 +373,7 @@ private void repopulateAccountList(Account[] accounts) { // add all accounts to list for (int i = 0; i < accounts.length; i++) { try { - mNavigationView.getMenu().add( + MenuItem accountMenuItem = mNavigationView.getMenu().add( R.id.drawer_menu_accounts, Menu.NONE, MENU_ORDER_ACCOUNT, @@ -382,6 +382,7 @@ private void repopulateAccountList(Account[] accounts) { accounts[i].name, mMenuAccountAvatarRadiusDimension) ); + setAvatar(accounts[i], accountMenuItem); } catch (Exception e) { Log_OC.e(TAG, "Error calculating RGB value for account menu item.", e); mNavigationView.getMenu().add( @@ -519,6 +520,62 @@ userIcon, getStorageManager(), account } } + /** + * fetches and sets the avatar of the current account in the drawer in case the drawer is available. + * + * @param account the account to be set in the drawer + * @param menuItem the menuItem to set the avatar on + */ + private void setAvatar(Account account, MenuItem menuItem) { + if (mDrawerLayout != null && account != null) { + int lastAtPos = account.name.lastIndexOf("@"); + String username = account.name.substring(0, lastAtPos); + + // Thumbnail in Cache? + Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + username); + + if (thumbnail != null) { + RoundedBitmapDrawable roundedAvatar = RoundedBitmapDrawableFactory.create + (MainApp.getAppContext().getResources(), thumbnail); + roundedAvatar.setCircular(true); + menuItem.setIcon(roundedAvatar); + } else { + // generate new avatar + if (ThumbnailsCacheManager.cancelPotentialAvatarWork(username, menuItem)) { + final ThumbnailsCacheManager.AvatarGenerationTask task = + new ThumbnailsCacheManager.AvatarGenerationTask( + menuItem, getStorageManager(), account + ); + if (thumbnail == null) { + try { + menuItem.setIcon( + TextDrawable.createAvatar( + account.name, + mMenuAccountAvatarRadiusDimension + ) + ); + } catch (Exception e) { + Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); + menuItem.setIcon(R.drawable.ic_account_circle); + } + } else { + final ThumbnailsCacheManager.AsyncAvatarDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncAvatarDrawable( + getResources(), + thumbnail, + task + ); + RoundedBitmapDrawable roundedAvatar = RoundedBitmapDrawableFactory.create + (MainApp.getAppContext().getResources(), asyncDrawable.getBitmap()); + roundedAvatar.setCircular(true); + menuItem.setIcon(roundedAvatar); + } + task.execute(username); + } + } + } + } + /** * Toggle between standard menu and account list including saving the state. */ From a4af5e990132488b4f36923b7544ad1e42a2d1bc Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 7 Apr 2016 11:28:58 +0200 Subject: [PATCH 51/84] fixing the thumbnail caching (user's name can be the same on two different servers), minor housekeeping for account name parsing --- .../android/authentication/AccountUtils.java | 14 ++++++ .../datamodel/ThumbnailsCacheManager.java | 8 +-- .../android/ui/activity/DrawerActivity.java | 50 ++++++++----------- .../owncloud/android/utils/BitmapUtils.java | 22 ++++++-- 4 files changed, 57 insertions(+), 37 deletions(-) diff --git a/src/com/owncloud/android/authentication/AccountUtils.java b/src/com/owncloud/android/authentication/AccountUtils.java index 71e211f88f8..14220be79bf 100644 --- a/src/com/owncloud/android/authentication/AccountUtils.java +++ b/src/com/owncloud/android/authentication/AccountUtils.java @@ -110,6 +110,20 @@ public static boolean exists(Account account, Context context) { } return false; } + + /** + * returns the user's name based on the account name. + * + * @param accountName the account name + * @return the user's name + */ + public static String getUsernameOfAccount(String accountName) { + if (accountName != null) { + return accountName.substring(0, accountName.lastIndexOf("@")); + } else { + return null; + } + } /** * Returns owncloud account identified by accountName or null if it does not exist. diff --git a/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index 33c44061825..899f6510758 100644 --- a/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -32,8 +32,6 @@ import android.media.ThumbnailUtils; import android.net.Uri; import android.os.AsyncTask; -import android.support.v4.graphics.drawable.RoundedBitmapDrawable; -import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; import android.view.MenuItem; import android.widget.ImageView; @@ -509,7 +507,7 @@ private Bitmap doAvatarInBackground() { GetMethod get = null; try { String uri = mClient.getBaseUri() + "" + - "/index.php/avatar/" + username + "/" + px; + "/index.php/avatar/" + AccountUtils.getUsernameOfAccount(username) + "/" + px; Log_OC.d("Avatar", "URI: " + uri); get = new GetMethod(uri); int status = mClient.executeMethod(get); @@ -648,9 +646,7 @@ public static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { public static AvatarGenerationTask getAvatarWorkerTask(ImageView imageView) { if (imageView != null) { - if (imageView != null) { - return getAvatarWorkerTask(imageView.getDrawable()); - } + return getAvatarWorkerTask(imageView.getDrawable()); } return null; } diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index c47e55466e6..cba44d71282 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -47,6 +47,7 @@ import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.TextDrawable; +import com.owncloud.android.utils.BitmapUtils; /** * Base class to handle setup of the drawer implementation including user switching and avatar fetching and fallback @@ -443,8 +444,7 @@ protected void setAccountInDrawer(Account account) { } catch (Exception e) { Log_OC.w(TAG, "Couldn't read display name of account; using account name instead"); - int lastAtPos = account.name.lastIndexOf("@"); - username.setText(account.name.substring(0, lastAtPos)); + username.setText(AccountUtils.getUsernameOfAccount(account.name)); } setAvatar(account, R.id.drawer_current_account, true); @@ -460,23 +460,20 @@ protected void setAccountInDrawer(Account account) { */ private void setAvatar(Account account, int avatarViewId, boolean currentAccount) { if (mDrawerLayout != null && account != null) { - int lastAtPos = account.name.lastIndexOf("@"); - String username = account.name.substring(0, lastAtPos); ImageView userIcon = (ImageView) findNavigationViewChildById(avatarViewId); userIcon.setContentDescription(account.name); // Thumbnail in Cache? - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + username); + Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + account.name); if (thumbnail != null) { - RoundedBitmapDrawable roundedAvatar = RoundedBitmapDrawableFactory.create - (MainApp.getAppContext().getResources(), thumbnail); - roundedAvatar.setCircular(true); - userIcon.setImageDrawable(roundedAvatar); + userIcon.setImageDrawable( + BitmapUtils.bitmapToCircularBitmapDrawable(MainApp.getAppContext().getResources(), thumbnail) + ); } else { // generate new avatar - if (ThumbnailsCacheManager.cancelPotentialAvatarWork(username, userIcon)) { + if (ThumbnailsCacheManager.cancelPotentialAvatarWork(account.name, userIcon)) { final ThumbnailsCacheManager.AvatarGenerationTask task = new ThumbnailsCacheManager.AvatarGenerationTask( userIcon, getStorageManager(), account @@ -509,12 +506,12 @@ userIcon, getStorageManager(), account thumbnail, task ); - RoundedBitmapDrawable roundedAvatar = RoundedBitmapDrawableFactory.create - (MainApp.getAppContext().getResources(), asyncDrawable.getBitmap()); - roundedAvatar.setCircular(true); - userIcon.setImageDrawable(roundedAvatar); + userIcon.setImageDrawable( + BitmapUtils.bitmapToCircularBitmapDrawable( + MainApp.getAppContext().getResources(), asyncDrawable.getBitmap()) + ); } - task.execute(username); + task.execute(account.name); } } } @@ -528,20 +525,17 @@ userIcon, getStorageManager(), account */ private void setAvatar(Account account, MenuItem menuItem) { if (mDrawerLayout != null && account != null) { - int lastAtPos = account.name.lastIndexOf("@"); - String username = account.name.substring(0, lastAtPos); // Thumbnail in Cache? - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + username); + Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + account.name); if (thumbnail != null) { - RoundedBitmapDrawable roundedAvatar = RoundedBitmapDrawableFactory.create - (MainApp.getAppContext().getResources(), thumbnail); - roundedAvatar.setCircular(true); - menuItem.setIcon(roundedAvatar); + menuItem.setIcon( + BitmapUtils.bitmapToCircularBitmapDrawable(MainApp.getAppContext().getResources(), thumbnail) + ); } else { // generate new avatar - if (ThumbnailsCacheManager.cancelPotentialAvatarWork(username, menuItem)) { + if (ThumbnailsCacheManager.cancelPotentialAvatarWork(account.name, menuItem)) { final ThumbnailsCacheManager.AvatarGenerationTask task = new ThumbnailsCacheManager.AvatarGenerationTask( menuItem, getStorageManager(), account @@ -565,12 +559,12 @@ menuItem, getStorageManager(), account thumbnail, task ); - RoundedBitmapDrawable roundedAvatar = RoundedBitmapDrawableFactory.create - (MainApp.getAppContext().getResources(), asyncDrawable.getBitmap()); - roundedAvatar.setCircular(true); - menuItem.setIcon(roundedAvatar); + menuItem.setIcon( + BitmapUtils.bitmapToCircularBitmapDrawable( + MainApp.getAppContext().getResources(), asyncDrawable.getBitmap()) + ); } - task.execute(username); + task.execute(account.name); } } } diff --git a/src/com/owncloud/android/utils/BitmapUtils.java b/src/com/owncloud/android/utils/BitmapUtils.java index 84d7b5a992c..263e9c199d4 100644 --- a/src/com/owncloud/android/utils/BitmapUtils.java +++ b/src/com/owncloud/android/utils/BitmapUtils.java @@ -19,14 +19,18 @@ */ package com.owncloud.android.utils; +import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.lib.common.utils.Log_OC; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.graphics.BitmapFactory.Options; import android.media.ExifInterface; import android.net.Uri; +import android.support.v4.graphics.drawable.RoundedBitmapDrawable; +import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; import android.webkit.MimeTypeMap; import java.io.File; @@ -75,7 +79,6 @@ public static Bitmap decodeSampledBitmapFromFile(String srcPath, int reqWidth, i // decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(srcPath, options); - } @@ -282,8 +285,7 @@ public static boolean isImage(File file) { */ public static int[] calculateRGB(String accountName) throws UnsupportedEncodingException, NoSuchAlgorithmException { // using adapted algorithm from /core/js/placeholder.js:50 - int lastAtPos = accountName.lastIndexOf("@"); - String username = accountName.substring(0, lastAtPos); + String username = AccountUtils.getUsernameOfAccount(accountName); byte[] seed = username.getBytes("UTF-8"); MessageDigest md = MessageDigest.getInstance("MD5"); // Integer seedMd5Int = Math.abs(new String(Hex.encodeHex(seedMd5)).hashCode()); @@ -295,4 +297,18 @@ public static int[] calculateRGB(String accountName) throws UnsupportedEncodingE return BitmapUtils.HSLtoRGB(hue, 90.0f, 65.0f, 1.0f); } + + /** + * Returns a new circular bitmap drawable by creating it from a bitmap, setting initial target density based on + * the display metrics of the resources. + * + * @param resources the resources for initial target density + * @param bitmap the original bitmap + * @return the circular bitmap + */ + public static RoundedBitmapDrawable bitmapToCircularBitmapDrawable(Resources resources, Bitmap bitmap) { + RoundedBitmapDrawable roundedBitmap = RoundedBitmapDrawableFactory.create(resources, bitmap); + roundedBitmap.setCircular(true); + return roundedBitmap; + } } From 8dca95cf6137f59cfd78307e96a93d9328cbde77 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 7 Apr 2016 18:11:37 +0200 Subject: [PATCH 52/84] avatar loading for account manager too + house keeping --- res/layout/account_action.xml | 46 +++++++ res/layout/account_item.xml | 2 +- .../android/ui/activity/DrawerActivity.java | 114 +++++------------- .../ui/activity/ManageAccountsActivity.java | 7 +- .../ui/adapter/AccountListAdapter.java | 19 ++- .../owncloud/android/utils/DisplayUtils.java | 57 +++++++++ 6 files changed, 145 insertions(+), 100 deletions(-) create mode 100644 res/layout/account_action.xml diff --git a/res/layout/account_action.xml b/res/layout/account_action.xml new file mode 100644 index 00000000000..2c5e2668a29 --- /dev/null +++ b/res/layout/account_action.xml @@ -0,0 +1,46 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/layout/account_item.xml b/res/layout/account_item.xml index d7616502011..9094173a688 100644 --- a/res/layout/account_item.xml +++ b/res/layout/account_item.xml @@ -33,7 +33,7 @@ 0 && mNavigationView != null) { - repopulateAccountList(accounts); - setAccountInDrawer(AccountUtils.getCurrentOwnCloudAccount(this)); - populateDrawerOwnCloudAccounts(); - - // activate second/end account avatar - if (mAvatars[1] != null) { - setAvatar(mAvatars[1], R.id.drawer_account_end, false); - mAccountEndAccountAvatar.setVisibility(View.VISIBLE); - } else { - mAccountEndAccountAvatar.setVisibility(View.GONE); - } + if (mNavigationView != null && mDrawerLayout != null) { + if (accounts.length > 0) { + repopulateAccountList(accounts); + setAccountInDrawer(AccountUtils.getCurrentOwnCloudAccount(this)); + populateDrawerOwnCloudAccounts(); + + // activate second/end account avatar + if (mAvatars[1] != null) { + DisplayUtils.setAvatar(mAvatars[1], + (ImageView) findNavigationViewChildById(R.id.drawer_account_end), + mOtherAccountAvatarRadiusDimension, getResources(), getStorageManager()); + mAccountEndAccountAvatar.setVisibility(View.VISIBLE); + } else { + mAccountEndAccountAvatar.setVisibility(View.GONE); + } - // activate third/middle account avatar - if (mAvatars[2] != null) { - setAvatar(mAvatars[2], R.id.drawer_account_middle, false); - mAccountMiddleAccountAvatar.setVisibility(View.VISIBLE); + // activate third/middle account avatar + if (mAvatars[2] != null) { + DisplayUtils.setAvatar(mAvatars[2], + (ImageView) findNavigationViewChildById(R.id.drawer_account_middle), + mOtherAccountAvatarRadiusDimension, getResources(), getStorageManager()); + mAccountMiddleAccountAvatar.setVisibility(View.VISIBLE); + } else { + mAccountMiddleAccountAvatar.setVisibility(View.GONE); + } } else { + mAccountEndAccountAvatar.setVisibility(View.GONE); mAccountMiddleAccountAvatar.setVisibility(View.GONE); } - } else { - mAccountEndAccountAvatar.setVisibility(View.GONE); - mAccountMiddleAccountAvatar.setVisibility(View.GONE); } } @@ -447,73 +454,8 @@ protected void setAccountInDrawer(Account account) { username.setText(AccountUtils.getUsernameOfAccount(account.name)); } - setAvatar(account, R.id.drawer_current_account, true); - } - } - - /** - * fetches and sets the avatar of the current account in the drawer in case the drawer is available. - * - * @param account the account to be set in the drawer - * @param avatarViewId the view to set the avatar on - * @param currentAccount flag if it is the current avatar or another (impacts chosen size) - */ - private void setAvatar(Account account, int avatarViewId, boolean currentAccount) { - if (mDrawerLayout != null && account != null) { - - ImageView userIcon = (ImageView) findNavigationViewChildById(avatarViewId); - userIcon.setContentDescription(account.name); - - // Thumbnail in Cache? - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + account.name); - - if (thumbnail != null) { - userIcon.setImageDrawable( - BitmapUtils.bitmapToCircularBitmapDrawable(MainApp.getAppContext().getResources(), thumbnail) - ); - } else { - // generate new avatar - if (ThumbnailsCacheManager.cancelPotentialAvatarWork(account.name, userIcon)) { - final ThumbnailsCacheManager.AvatarGenerationTask task = - new ThumbnailsCacheManager.AvatarGenerationTask( - userIcon, getStorageManager(), account - ); - if (thumbnail == null) { - try { - if (currentAccount) { - userIcon.setImageDrawable( - TextDrawable.createAvatar( - account.name, - mCurrentAccountAvatarRadiusDimension - ) - ); - } else { - userIcon.setImageDrawable( - TextDrawable.createAvatar( - account.name, - mOtherAccountAvatarRadiusDimension - ) - ); - } - } catch (Exception e) { - Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); - userIcon.setImageResource(R.drawable.ic_account_circle); - } - } else { - final ThumbnailsCacheManager.AsyncAvatarDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncAvatarDrawable( - getResources(), - thumbnail, - task - ); - userIcon.setImageDrawable( - BitmapUtils.bitmapToCircularBitmapDrawable( - MainApp.getAppContext().getResources(), asyncDrawable.getBitmap()) - ); - } - task.execute(account.name); - } - } + DisplayUtils.setAvatar(account, (ImageView) findNavigationViewChildById(R.id.drawer_current_account), + mCurrentAccountAvatarRadiusDimension, getResources(), getStorageManager()); } } diff --git a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java index 4d3c5bf694a..ec10ab4976a 100644 --- a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -54,7 +54,7 @@ /** * An Activity that allows the user to manage accounts. */ -public class ManageAccountsActivity extends ToolbarActivity +public class ManageAccountsActivity extends FileActivity implements AccountListAdapter.AccountListAdapterListener, AccountManagerCallback, ComponentsGetter { private static final String TAG = ManageAccountsActivity.class.getSimpleName(); public static final String KEY_ACCOUNT_LIST_CHANGED = "ACCOUNT_LIST_CHANGED"; @@ -86,6 +86,9 @@ protected void onCreate(Bundle savedInstanceState) { mOriginalAccounts = toAccountNameSet(accountList); mOriginalCurrentAccount = AccountUtils.getCurrentOwnCloudAccount(this).name; + setAccount(AccountUtils.getCurrentOwnCloudAccount(this)); + onAccountSet(false); + mAccountListAdapter = new AccountListAdapter(this, getAccountListItems()); mListView.setAdapter(mAccountListAdapter); @@ -301,7 +304,7 @@ public OperationsService.OperationsServiceBinder getOperationsServiceBinder() { @Override public FileDataStorageManager getStorageManager() { - return null; + return super.getStorageManager(); } @Override diff --git a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java index d71fb2b6e1a..70b6373a21b 100644 --- a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java @@ -21,6 +21,7 @@ import android.accounts.Account; import android.content.Context; +import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -32,6 +33,7 @@ import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.TextDrawable; +import com.owncloud.android.ui.activity.BaseActivity; import com.owncloud.android.ui.activity.ManageAccountsActivity; import com.owncloud.android.utils.DisplayUtils; @@ -43,11 +45,11 @@ public class AccountListAdapter extends ArrayAdapter { private static final String TAG = AccountListAdapter.class.getSimpleName(); private float mAccountAvatarRadiusDimension; - private final Context mContext; + private final BaseActivity mContext; private List mValues; private AccountListAdapterListener mListener; - public AccountListAdapter(Context context, List values) { + public AccountListAdapter(BaseActivity context, List values) { super(context, -1, values); this.mContext = context; this.mValues = values; @@ -64,7 +66,7 @@ public View getView(final int position, View convertView, ViewGroup parent) { AccountViewHolderItem viewHolder; if (convertView == null) { - LayoutInflater inflater = ((ManageAccountsActivity) mContext).getLayoutInflater(); + LayoutInflater inflater = mContext.getLayoutInflater(); convertView = inflater.inflate(R.layout.account_item, parent, false); viewHolder = new AccountViewHolderItem(); @@ -105,11 +107,8 @@ public View getView(final int position, View convertView, ViewGroup parent) { viewHolder.textViewItem.setTag(account.name); try { - TextDrawable icon = TextDrawable.createAvatar( - account.name, - mAccountAvatarRadiusDimension - ); - viewHolder.imageViewItem.setImageDrawable(icon); + DisplayUtils.setAvatar(account, viewHolder.imageViewItem, mAccountAvatarRadiusDimension, + mContext.getResources(), mContext.getStorageManager()); } catch (Exception e) { Log_OC.e(TAG, "Error calculating RGB value for account list item.", e); // use user icon as a fallback @@ -136,11 +135,9 @@ public void onClick(View v) { } // create add account action item else if (AccountListItem.TYPE_ACTION_ADD == accountListItem.getType()) { LayoutInflater inflater = ((ManageAccountsActivity) mContext).getLayoutInflater(); - View actionView = inflater.inflate(R.layout.account_item, parent, false); + View actionView = inflater.inflate(R.layout.account_action, parent, false); ((TextView) actionView.findViewById(R.id.user_name)).setText(R.string.prefs_add_account); ((ImageView) actionView.findViewById(R.id.user_icon)).setImageResource(R.drawable.ic_account_plus); - actionView.findViewById(R.id.passwordButton).setVisibility(View.GONE); - actionView.findViewById(R.id.removeButton).setVisibility(View.GONE); // bind action listener actionView.setOnClickListener(new View.OnClickListener() { diff --git a/src/com/owncloud/android/utils/DisplayUtils.java b/src/com/owncloud/android/utils/DisplayUtils.java index b19f1b9b4be..2e4d68ac463 100644 --- a/src/com/owncloud/android/utils/DisplayUtils.java +++ b/src/com/owncloud/android/utils/DisplayUtils.java @@ -22,9 +22,12 @@ package com.owncloud.android.utils; +import android.accounts.Account; import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.PorterDuff; import android.os.Build; @@ -33,13 +36,18 @@ import android.text.format.DateUtils; import android.view.Display; import android.view.View; +import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.TextView; import com.owncloud.android.MainApp; import com.owncloud.android.R; +import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.ThumbnailsCacheManager; +import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.ui.TextDrawable; import java.math.BigDecimal; import java.net.IDN; @@ -53,6 +61,7 @@ * A helper class for some string operations. */ public class DisplayUtils { + private static final String TAG = DisplayUtils.class.getSimpleName(); private static final String OWNCLOUD_APP_NAME = "ownCloud"; @@ -293,4 +302,52 @@ public static void colorSnackbar(Context context, Snackbar snackbar) { // Changing action button text color snackbar.setActionTextColor(ContextCompat.getColor(context, R.color.white)); } + + /** + * fetches and sets the avatar of the current account in the drawer in case the drawer is available. + * + * @param account the account to be set in the drawer + * @param userIcon the image view to set the avatar on + * @param avatarRadius the avatar radius + * @param resources reference for density information + * @param storageManager reference for caching purposes + */ + public static void setAvatar(Account account, ImageView userIcon, float avatarRadius, Resources resources, + FileDataStorageManager storageManager) { + if (account != null) { + + userIcon.setContentDescription(account.name); + + // Thumbnail in Cache? + Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + account.name); + + if (thumbnail != null) { + userIcon.setImageDrawable( + BitmapUtils.bitmapToCircularBitmapDrawable(MainApp.getAppContext().getResources(), thumbnail) + ); + } else { + // generate new avatar + if (ThumbnailsCacheManager.cancelPotentialAvatarWork(account.name, userIcon)) { + final ThumbnailsCacheManager.AvatarGenerationTask task = + new ThumbnailsCacheManager.AvatarGenerationTask(userIcon, storageManager, account); + if (thumbnail == null) { + try { + userIcon.setImageDrawable(TextDrawable.createAvatar(account.name, avatarRadius)); + } catch (Exception e) { + Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); + userIcon.setImageResource(R.drawable.ic_account_circle); + } + } else { + final ThumbnailsCacheManager.AsyncAvatarDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncAvatarDrawable(resources, thumbnail, task); + userIcon.setImageDrawable( + BitmapUtils.bitmapToCircularBitmapDrawable( + MainApp.getAppContext().getResources(), asyncDrawable.getBitmap()) + ); + } + task.execute(account.name); + } + } + } + } } From 06a332aa0e14b0df6d4fce2620b623d6cb6ee53c Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 7 Apr 2016 18:48:33 +0200 Subject: [PATCH 53/84] fixed size differences of generated and image based avatars --- res/values/dims.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/dims.xml b/res/values/dims.xml index 0c393285a2c..478481b82eb 100644 --- a/res/values/dims.xml +++ b/res/values/dims.xml @@ -29,7 +29,7 @@ 20dp 56dp - 14sp + 12sp 20dp 32dp From 2d2cc7d67fe14d8fa265853a5845a2cfc318a4a4 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Fri, 8 Apr 2016 12:19:28 +0200 Subject: [PATCH 54/84] add toolbar to log screen --- res/layout/log_send_file.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/res/layout/log_send_file.xml b/res/layout/log_send_file.xml index 6f94785b034..318ec0fdc7f 100644 --- a/res/layout/log_send_file.xml +++ b/res/layout/log_send_file.xml @@ -25,6 +25,9 @@ android:filterTouchesWhenObscured="true" > + + Date: Wed, 13 Apr 2016 16:52:35 +0200 Subject: [PATCH 55/84] BugFix Log screen Toolbar --- src/com/owncloud/android/ui/activity/LogHistoryActivity.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/owncloud/android/ui/activity/LogHistoryActivity.java b/src/com/owncloud/android/ui/activity/LogHistoryActivity.java index 55f30e82687..7b70b6f3ec3 100644 --- a/src/com/owncloud/android/ui/activity/LogHistoryActivity.java +++ b/src/com/owncloud/android/ui/activity/LogHistoryActivity.java @@ -50,7 +50,7 @@ import com.owncloud.android.utils.FileStorageUtils; -public class LogHistoryActivity extends AppCompatActivity { +public class LogHistoryActivity extends ToolbarActivity { private static final String MAIL_ATTACHMENT_TYPE = "text/plain"; @@ -70,6 +70,8 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.log_send_file); + setupToolbar(); + setTitle(getText(R.string.actionbar_logger)); getSupportActionBar().setDisplayHomeAsUpEnabled(true); Button deleteHistoryButton = (Button) findViewById(R.id.deleteLogHistoryButton); From cedd7154d320e7ab3d6e92c6678ce7a91118751a Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 14 Apr 2016 17:40:44 +0200 Subject: [PATCH 56/84] List divider Fix --- res/values-v21/styles.xml | 1 - res/values/styles.xml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/res/values-v21/styles.xml b/res/values-v21/styles.xml index edfd58ddad3..c312c3856ba 100644 --- a/res/values-v21/styles.xml +++ b/res/values-v21/styles.xml @@ -27,7 +27,6 @@ @style/Theme.ownCloud.Dialog @style/ownCloud.AlertDialog @style/ownCloud.SearchView - @color/transparent @@ -169,6 +168,7 @@ + + + diff --git a/res/values/styles.xml b/res/values/styles.xml index ccecf53f3a4..8296429c972 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -22,34 +22,28 @@ - - + - - - - + - + + + + + + - + From 80a9edb345a144e6bb8b2034d615ca49c81e0cb5 Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Mon, 3 Oct 2016 14:06:46 +0200 Subject: [PATCH 62/84] Fix action mode --- res/values/setup.xml | 4 ++-- res/values/styles.xml | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/res/values/setup.xml b/res/values/setup.xml index ff172f6f5f8..833a3eb1868 100644 --- a/res/values/setup.xml +++ b/res/values/setup.xml @@ -39,7 +39,7 @@ #FFFFFF #FFFFFF @color/primary - @color/owncloud_blue_accent + @color/color_accent @color/white #D6D7D7 @color/black @@ -49,7 +49,7 @@ #000000 - @color/owncloud_blue_accent + @color/primary_button_background_color @color/actionbar_start_color diff --git a/res/values/styles.xml b/res/values/styles.xml index 7aafc0695cf..968c7f12464 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -20,21 +20,29 @@ From 9a4269e62dd5b38a4dc2fa453857a75248d4ed51 Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Mon, 3 Oct 2016 14:54:11 +0200 Subject: [PATCH 63/84] Fix crash when a file is shared with OC app from an external app --- AndroidManifest.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 90840bdbac4..95cb3253d19 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -76,8 +76,7 @@ + android:excludeFromRecents="true"> From 61645109882a71a36c66d73f695a460efec9d6e4 Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Tue, 4 Oct 2016 10:48:41 +0200 Subject: [PATCH 64/84] Remove navigation drawer from PreviewImageActivity - awkward behaviour, and not so handy there --- res/layout/preview_image_activity.xml | 29 ++------------ res/layout/toolbar_standard.xml | 1 + res/values/styles.xml | 38 +++++++++---------- .../ui/preview/PreviewImageActivity.java | 4 +- .../ui/preview/PreviewImageFragment.java | 1 + 5 files changed, 25 insertions(+), 48 deletions(-) diff --git a/res/layout/preview_image_activity.xml b/res/layout/preview_image_activity.xml index 7eec8cf1ec2..cc46b95511b 100644 --- a/res/layout/preview_image_activity.xml +++ b/res/layout/preview_image_activity.xml @@ -17,30 +17,9 @@ along with this program. If not, see . --> - - - - - - - - - \ No newline at end of file + android:filterTouchesWhenObscured="true" + /> diff --git a/res/layout/toolbar_standard.xml b/res/layout/toolbar_standard.xml index ee9e83a161e..b3e486af9ea 100644 --- a/res/layout/toolbar_standard.xml +++ b/res/layout/toolbar_standard.xml @@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> + - + + - @@ -70,6 +78,14 @@ @color/actionbar_start_color + + + + + + - - - - - - + + + + + @@ -141,12 +152,6 @@ @drawable/top_progress_bar_indeterminate - - - - - - + + + + + + + + + + - - - - - - + - - - - - - - - - #777777 #000000 - - - - diff --git a/res/values-v9/versioned_styles.xml b/res/values-v9/versioned_styles.xml deleted file mode 100644 index 57bb396f5d9..00000000000 --- a/res/values-v9/versioned_styles.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - @@ -239,13 +234,17 @@ true true - - #777777 - #000000 - - + + + \ No newline at end of file diff --git a/res/values/versioned_styles.xml b/res/values/versioned_styles.xml deleted file mode 100644 index 660e0fdc9fa..00000000000 --- a/res/values/versioned_styles.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - From 2e29e0f320c04a41c5da14247103f05dc4fee8cd Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Wed, 5 Oct 2016 10:10:15 +0200 Subject: [PATCH 68/84] Fix Toolbar in LocalFolderPickerActivity (to select camera folder) --- .../android/ui/activity/LocalFolderPickerActivity.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/owncloud/android/ui/activity/LocalFolderPickerActivity.java b/src/com/owncloud/android/ui/activity/LocalFolderPickerActivity.java index 6cfa71732ad..0d70f7a8672 100644 --- a/src/com/owncloud/android/ui/activity/LocalFolderPickerActivity.java +++ b/src/com/owncloud/android/ui/activity/LocalFolderPickerActivity.java @@ -43,7 +43,7 @@ * Displays local folders and let the user choose one of them, which path is set as result. */ -public class LocalFolderPickerActivity extends AppCompatActivity implements LocalFileListFragment.ContainerActivity { +public class LocalFolderPickerActivity extends ToolbarActivity implements LocalFileListFragment.ContainerActivity { private static final String TAG = LocalFolderPickerActivity.class.getName(); @@ -122,6 +122,8 @@ public void onClick(View view) { } }); + // init toolbar + setupToolbar(); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayShowTitleEnabled(true); From 83673dbce4afb2e721d71e7d480b9aa8f8d1a94f Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Wed, 5 Oct 2016 10:13:12 +0200 Subject: [PATCH 69/84] Set style with old ActionBar to some less-used Activities to grant correct operation --- AndroidManifest.xml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 5dbaf6d9099..097ad01c7fd 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -167,7 +167,7 @@ android:name=".authentication.AuthenticatorActivity" android:exported="true" android:launchMode="singleTask" - android:theme="@style/Theme.ownCloud.Toolbar" > + > @@ -188,10 +188,13 @@ - - + + - + From 4aac26e1dac2935f4b84a5c654e78f2f9e6d6bab Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Wed, 5 Oct 2016 13:19:20 +0200 Subject: [PATCH 70/84] Flat layout for drawer header --- res/layout/drawer_header.xml | 197 +++++++++++++++++------------------ 1 file changed, 95 insertions(+), 102 deletions(-) diff --git a/res/layout/drawer_header.xml b/res/layout/drawer_header.xml index 1b573d63335..8e2e69f0272 100644 --- a/res/layout/drawer_header.xml +++ b/res/layout/drawer_header.xml @@ -16,108 +16,101 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + android:layout_alignParentBottom="true" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" + android:contentDescription="@string/drawer_manage_accounts" + android:src="@drawable/ic_down" + /> + + From 2937ce17726547e58e553718058fe3f4210f2481 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Wed, 5 Oct 2016 13:42:02 +0200 Subject: [PATCH 71/84] renamed TextDrawable-->DefaultAvatrTextDrawable, renamed inc-->GmbH --- res/layout/account_action.xml | 2 +- res/layout/account_item.xml | 2 +- res/layout/accounts_layout.xml | 2 +- res/layout/activity_row.xml | 2 +- res/layout/drawer_header.xml | 2 +- res/layout/generic_explanation.xml | 2 +- res/layout/toolbar_standard.xml | 2 +- res/layout/upload_list_layout.xml | 2 +- res/menu/drawer_menu.xml | 2 +- res/values-sw360dp/dims.xml | 2 +- ...wable.java => DefaultAvatarTextDrawable.java} | 16 ++++++++-------- .../android/ui/activity/DrawerActivity.java | 10 ++++------ .../ui/activity/ManageAccountsActivity.java | 2 +- .../android/ui/activity/ToolbarActivity.java | 2 +- .../android/ui/adapter/AccountListAdapter.java | 5 +---- .../android/ui/adapter/AccountListItem.java | 2 +- src/com/owncloud/android/utils/DisplayUtils.java | 6 ++---- 17 files changed, 28 insertions(+), 35 deletions(-) rename src/com/owncloud/android/ui/{TextDrawable.java => DefaultAvatarTextDrawable.java} (85%) diff --git a/res/layout/account_action.xml b/res/layout/account_action.xml index 2c5e2668a29..4afa8b9f71f 100644 --- a/res/layout/account_action.xml +++ b/res/layout/account_action.xml @@ -2,7 +2,7 @@ diff --git a/res/layout/account_item.xml b/res/layout/account_item.xml index 7d19ec45df4..a792dd7e840 100644 --- a/res/layout/account_item.xml +++ b/res/layout/account_item.xml @@ -18,27 +18,30 @@ --> @@ -60,9 +63,11 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:paddingLeft="@dimen/standard_half_padding" + android:paddingStart="@dimen/standard_half_padding" android:paddingTop="@dimen/standard_padding" android:paddingBottom="@dimen/standard_padding" android:paddingRight="@dimen/standard_padding" - android:src="@drawable/ic_close"/> + android:paddingEnd="@dimen/standard_padding" + android:src="@drawable/ic_action_delete_grey"/> \ No newline at end of file diff --git a/res/layout/accounts_layout.xml b/res/layout/accounts_layout.xml index 6467d86c3bd..b9540b366da 100644 --- a/res/layout/accounts_layout.xml +++ b/res/layout/accounts_layout.xml @@ -17,10 +17,10 @@ along with this program. If not, see . --> diff --git a/res/layout/action_item.xml b/res/layout/action_item.xml index 7783f59aa8b..318fe56cd79 100644 --- a/res/layout/action_item.xml +++ b/res/layout/action_item.xml @@ -20,7 +20,7 @@ @@ -56,7 +56,7 @@ diff --git a/res/layout/uploader_layout.xml b/res/layout/uploader_layout.xml index 9171c25c9fe..b644a7109c2 100644 --- a/res/layout/uploader_layout.xml +++ b/res/layout/uploader_layout.xml @@ -20,8 +20,8 @@ @@ -31,7 +31,7 @@ @@ -46,17 +46,10 @@ - - - - - + android:layout_height="1dp" + android:src="@drawable/uploader_list_separator"/> + android:title="@string/drawer_item_uploads_list"/> - + android:layout_height="80dp" + > + + + + android:textSize="16sp" + android:textStyle="bold" + android:maxLines="1" + android:ellipsize="end"/> + + - \ No newline at end of file + \ No newline at end of file diff --git a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java index bd865e259ba..481bd8a6c11 100644 --- a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java @@ -28,10 +28,10 @@ import android.widget.TextView; import com.owncloud.android.R; +import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.activity.BaseActivity; -import com.owncloud.android.ui.activity.ManageAccountsActivity; import com.owncloud.android.utils.DisplayUtils; import java.util.List; @@ -67,8 +67,9 @@ public View getView(final int position, View convertView, ViewGroup parent) { convertView = inflater.inflate(R.layout.account_item, parent, false); viewHolder = new AccountViewHolderItem(); - viewHolder.textViewItem = (TextView) convertView.findViewById(R.id.action_name); - viewHolder.imageViewItem = (ImageView) convertView.findViewById(R.id.action_icon); + viewHolder.nameViewItem = (TextView) convertView.findViewById(R.id.name); + viewHolder.accountViewItem = (TextView) convertView.findViewById(R.id.account); + viewHolder.imageViewItem = (ImageView) convertView.findViewById(R.id.icon); viewHolder.passwordButtonItem = (ImageView) convertView.findViewById(R.id.passwordButton); viewHolder.removeButtonItem = (ImageView) convertView.findViewById(R.id.removeButton); @@ -86,22 +87,21 @@ public View getView(final int position, View convertView, ViewGroup parent) { Account account = accountListItem.getAccount(); try { OwnCloudAccount oca = new OwnCloudAccount(account, mContext); - viewHolder.textViewItem.setText( - oca.getDisplayName() + " @ " + - DisplayUtils.convertIdn( - account.name.substring(account.name.lastIndexOf("@") + 1), - false - ) - ); + viewHolder.nameViewItem.setText(oca.getDisplayName()); } catch (Exception e) { Log_OC.w( TAG, "Account not found right after being read :\\ ; using account name instead of display name" ); - // Handle internationalized domain names - viewHolder.textViewItem.setText(DisplayUtils.convertIdn(account.name, false)); + viewHolder.nameViewItem.setText( + AccountUtils.getUsernameOfAccount(account.name) + ); } - viewHolder.textViewItem.setTag(account.name); + viewHolder.nameViewItem.setTag(account.name); + + viewHolder.accountViewItem.setText( + DisplayUtils.convertIdn(account.name, false) + ); try { DisplayUtils.setAvatar(account, viewHolder.imageViewItem, mAccountAvatarRadiusDimension, @@ -131,10 +131,10 @@ public void onClick(View v) { }); } // create add account action item else if (AccountListItem.TYPE_ACTION_ADD == accountListItem.getType()) { - LayoutInflater inflater = ((ManageAccountsActivity) mContext).getLayoutInflater(); + LayoutInflater inflater = mContext.getLayoutInflater(); View actionView = inflater.inflate(R.layout.account_action, parent, false); - ((TextView) actionView.findViewById(R.id.action_name)).setText(R.string.prefs_add_account); - ((ImageView) actionView.findViewById(R.id.action_icon)).setImageResource(R.drawable.ic_account_plus); + ((TextView) actionView.findViewById(R.id.name)).setText(R.string.prefs_add_account); + ((ImageView) actionView.findViewById(R.id.icon)).setImageResource(R.drawable.ic_account_plus); // bind action listener actionView.setOnClickListener(new View.OnClickListener() { @@ -166,7 +166,8 @@ public interface AccountListAdapterListener { * Account ViewHolderItem to get smooth scrolling. */ static class AccountViewHolderItem { - TextView textViewItem; + TextView nameViewItem; + TextView accountViewItem; ImageView imageViewItem; ImageView passwordButtonItem; From 4c9a38aea28463c984f317b29b1b3ec41a2d7c5d Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Fri, 7 Oct 2016 13:51:02 +0200 Subject: [PATCH 78/84] Add confirmation dialog to remove account --- res/values/strings.xml | 1 + .../ui/activity/ManageAccountsActivity.java | 16 ++- .../ui/adapter/AccountListAdapter.java | 6 - .../dialog/RemoveAccountDialogFragment.java | 117 ++++++++++++++++++ 4 files changed, 129 insertions(+), 11 deletions(-) create mode 100644 src/com/owncloud/android/ui/dialog/RemoveAccountDialogFragment.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 6b157644fbe..d59ae380bb5 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -236,6 +236,7 @@ "Do you really want to remove %1$s?" "Do you really want to remove %1$s and its contents?" Local only + "Do you really want to remove the account %1$s?" "Removal succeeded" "Removal failed" Enter a new name diff --git a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java index dae76596b93..b5de6474dae 100644 --- a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -45,6 +45,7 @@ import com.owncloud.android.services.OperationsService; import com.owncloud.android.ui.adapter.AccountListAdapter; import com.owncloud.android.ui.adapter.AccountListItem; +import com.owncloud.android.ui.dialog.RemoveAccountDialogFragment; import com.owncloud.android.ui.helpers.FileOperationsHelper; import java.util.ArrayList; @@ -55,7 +56,11 @@ * An Activity that allows the user to manage accounts. */ public class ManageAccountsActivity extends FileActivity - implements AccountListAdapter.AccountListAdapterListener, AccountManagerCallback, ComponentsGetter { + implements + AccountListAdapter.AccountListAdapterListener, + AccountManagerCallback, + ComponentsGetter { + private static final String TAG = ManageAccountsActivity.class.getSimpleName(); public static final String KEY_ACCOUNT_LIST_CHANGED = "ACCOUNT_LIST_CHANGED"; public static final String KEY_CURRENT_ACCOUNT_CHANGED = "CURRENT_ACCOUNT_CHANGED"; @@ -196,10 +201,11 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void removeAccount(Account account) { - AccountManager am = (AccountManager) this.getSystemService(ACCOUNT_SERVICE); mAccountName = account.name; - am.removeAccount(account, ManageAccountsActivity.this, mHandler); - Log_OC.d(TAG, "Remove an account " + account.name); + RemoveAccountDialogFragment dialog = RemoveAccountDialogFragment.newInstance( + account + ); + dialog.show(getSupportFragmentManager(), RemoveAccountDialogFragment.FTAG_CONFIRMATION); } @Override @@ -266,7 +272,7 @@ public void run(AccountManagerFuture future) { } mAccountListAdapter = new AccountListAdapter(this, getAccountListItems()); - mAccountListAdapter.notifyDataSetChanged(); + mListView.setAdapter(mAccountListAdapter); AccountManager am = AccountManager.get(this); if (am.getAccountsByType(MainApp.getAccountType()).length == 0) { diff --git a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java index 481bd8a6c11..de8fde05b3a 100644 --- a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java @@ -54,10 +54,6 @@ public AccountListAdapter(BaseActivity context, List values) { this.mAccountAvatarRadiusDimension = context.getResources().getDimension(R.dimen.list_item_avatar_icon_radius); } - public void setAccountList(List values) { - this.mValues = values; - } - @Override public View getView(final int position, View convertView, ViewGroup parent) { AccountViewHolderItem viewHolder; @@ -125,8 +121,6 @@ public void onClick(View v) { @Override public void onClick(View v) { mListener.removeAccount(mValues.get(position).getAccount()); - mValues.remove(position); - AccountListAdapter.this.notifyDataSetChanged(); } }); } // create add account action item diff --git a/src/com/owncloud/android/ui/dialog/RemoveAccountDialogFragment.java b/src/com/owncloud/android/ui/dialog/RemoveAccountDialogFragment.java new file mode 100644 index 00000000000..e831a476b8d --- /dev/null +++ b/src/com/owncloud/android/ui/dialog/RemoveAccountDialogFragment.java @@ -0,0 +1,117 @@ +/** + * ownCloud Android client application + * + * @author David A. Velasco + * Copyright (C) 2016 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android.ui.dialog; + +/** + * Dialog requiring confirmation before removing an OC Account. + * + * Removes the account if the user confirms. + * + * Container Activity needs to implement AccountManagerCallback. + */ + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.app.Activity; +import android.app.Dialog; +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.NonNull; + +import com.owncloud.android.R; +import com.owncloud.android.ui.dialog.ConfirmationDialogFragment.ConfirmationDialogFragmentListener; + +public class RemoveAccountDialogFragment extends ConfirmationDialogFragment + implements ConfirmationDialogFragmentListener { + + private Account mTargetAccount; + + private static final String ARG_TARGET_ACCOUNT = "TARGET_ACCOUNT"; + + /** + * Public factory method to create new RemoveAccountDialogFragment instances. + * + * @param account Account to remove. + * @return Dialog ready to show. + */ + public static RemoveAccountDialogFragment newInstance(Account account) { + if (account == null ) { + throw new IllegalArgumentException("Cannot remove a NULL account"); + } + + RemoveAccountDialogFragment frag = new RemoveAccountDialogFragment(); + Bundle args = new Bundle(); + args.putInt(ARG_MESSAGE_RESOURCE_ID, R.string.confirmation_remove_account_alert); + args.putStringArray(ARG_MESSAGE_ARGUMENTS, new String[]{account.name}); + args.putInt(ARG_POSITIVE_BTN_RES, R.string.common_yes); + args.putInt(ARG_NEUTRAL_BTN_RES, R.string.common_no); + args.putInt(ARG_NEGATIVE_BTN_RES, -1); + args.putParcelable(ARG_TARGET_ACCOUNT, account); + frag.setArguments(args); + + return frag; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + // checked here to fail soon in case of wrong usage + try { + AccountManagerCallback a = (AccountManagerCallback)getActivity(); + } catch (ClassCastException c) { + throw new IllegalStateException( + "Container Activity needs to implement (AccountManagerCallback)", c + ); + } + } + + @Override @NonNull + public Dialog onCreateDialog(Bundle savedInstanceState) { + Dialog dialog = super.onCreateDialog(savedInstanceState); + mTargetAccount = getArguments().getParcelable(ARG_TARGET_ACCOUNT); + + setOnConfirmationListener(this); + + return dialog; + } + + /** + * Performs the removal of the target account. + */ + @Override + public void onConfirmation(String callerTag) { + Activity parentActivity = getActivity(); + AccountManager am = AccountManager.get(parentActivity); + AccountManagerCallback callback = (AccountManagerCallback)parentActivity; + am.removeAccount(mTargetAccount, callback, new Handler()); + } + + @Override + public void onCancel(String callerTag) { + // nothing to do here + } + + @Override + public void onNeutral(String callerTag) { + // nothing to do here + } +} \ No newline at end of file From 5a131a274915e8bebd9807f2de91f2d5ea281768 Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Mon, 10 Oct 2016 11:16:24 +0200 Subject: [PATCH 79/84] Finish action mode when drawer starts to open, recover it when drawer is closed --- build.gradle | 7 +- .../android/ui/activity/DrawerActivity.java | 15 +- .../ui/fragment/OCFileListFragment.java | 237 +++++++++++++----- .../helpers/SparseBooleanArrayParcelable.java | 91 +++++++ 4 files changed, 289 insertions(+), 61 deletions(-) create mode 100644 src/com/owncloud/android/ui/helpers/SparseBooleanArrayParcelable.java diff --git a/build.gradle b/build.gradle index fbb79318f17..56afe93022a 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ buildscript { apply plugin: 'com.android.application' ext { - supportLibraryVersion = '23.1.1' + supportLibraryVersion = '24.2.1' } repositories { @@ -55,7 +55,10 @@ dependencies { androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2' // UIAutomator - for cross-app UI tests, and to grant screen is turned on in Espresso tests - androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1' + androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2' + + // fix conflict in dependencies; see http://g.co/androidstudio/app-test-app-conflict for details + androidTestCompile "com.android.support:support-annotations:${supportLibraryVersion}" } diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index 481f95af6e0..e1329c82e36 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -186,7 +186,7 @@ public void onDrawerOpened(View drawerView) { }; // Set the drawer toggle as the DrawerListener - mDrawerLayout.setDrawerListener(mDrawerToggle); + mDrawerLayout.addDrawerListener(mDrawerToggle); mDrawerToggle.setDrawerIndicatorEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true); } @@ -699,4 +699,17 @@ private void populateDrawerOwnCloudAccounts() { } } } + + /** + * Adds other listeners to react on changes of the drawer layout. + * + * @param listener Object interested in changes of the drawer layout. + */ + public void addDrawerListener(DrawerLayout.DrawerListener listener) { + if (mDrawerLayout != null) { + mDrawerLayout.addDrawerListener(listener); + } else { + Log_OC.e(TAG, "Drawer layout not ready to add drawer listener"); + } + } } diff --git a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java index 6cf666dc423..662f8aa6d0f 100644 --- a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -28,7 +28,9 @@ import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; +import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.SwipeRefreshLayout; +import android.util.SparseBooleanArray; import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; @@ -60,6 +62,7 @@ import com.owncloud.android.ui.dialog.CreateFolderDialogFragment; import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment; import com.owncloud.android.ui.dialog.RenameFileDialogFragment; +import com.owncloud.android.ui.helpers.SparseBooleanArrayParcelable; import com.owncloud.android.ui.preview.PreviewAudioFragment; import com.owncloud.android.ui.preview.PreviewImageFragment; import com.owncloud.android.ui.preview.PreviewTextFragment; @@ -103,6 +106,7 @@ public class OCFileListFragment extends ExtendedListFragment { private boolean mHideFab = true; private boolean miniFabClicked = false; private ActionMode mActiveActionMode; + private OCFileListFragment.MultiChoiceModeListener mMultiChoiceModeListener; /** @@ -168,7 +172,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa Bundle args = getArguments(); boolean allowContextualActions = (args != null) && args.getBoolean(ARG_ALLOW_CONTEXTUAL_MODE, false); if (allowContextualActions) { - setChoiceModeAsMultipleModal(); + setChoiceModeAsMultipleModal(savedInstanceState); } Log_OC.i(TAG, "onCreateView() end"); return v; @@ -359,79 +363,195 @@ private void removeFabLabels() { com.getbase.floatingactionbutton.R.id.fab_label)).setVisibility(View.GONE); } - private void setChoiceModeAsMultipleModal() { + /** + * Handler for multiple selection mode. + * + * Manages input from the user when one or more files or folders are selected in the list. + * + * Also listens to changes in navigation drawer to hide and recover multiple selection when it's opened + * and closed. + */ + private class MultiChoiceModeListener + implements AbsListView.MultiChoiceModeListener, DrawerLayout.DrawerListener { - setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); + private static final String KEY_ACTION_MODE_CLOSED_BY_DRAWER = "KILLED_ACTION_MODE"; + private static final String KEY_SELECTION_WHEN_CLOSED_BY_DRAWER = "CHECKED_ITEMS"; - setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() { + /** + * True when action mode is finished because the drawer was opened + */ + private boolean mActionModeClosedByDrawer = false; - @Override - public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { - getListView().invalidateViews(); - mode.invalidate(); - } + /** + * Selected items in list when action mode is closed by drawer + */ + private SparseBooleanArray mSelectionWhenActionModeClosedByDrawer = null; - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - mActiveActionMode = mode; - - MenuInflater inflater = getActivity().getMenuInflater(); - inflater.inflate(R.menu.file_actions_menu, menu); - mode.invalidate(); - - //set gray color - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - Window w = getActivity().getWindow(); - mStatusBarColor = w.getStatusBarColor(); - w.setStatusBarColor(mStatusBarColorActionMode); + @Override + public void onDrawerSlide(View drawerView, float slideOffset) { + // nothing to do + } + + @Override + public void onDrawerOpened(View drawerView) { + // nothing to do + } + + /** + * When the navigation drawer is closed, action mode is recovered in the same state as was + * when the drawer was (started to be) opened. + * + * @param drawerView Navigation drawer just closed. + */ + @Override + public void onDrawerClosed(View drawerView) { + if (mSelectionWhenActionModeClosedByDrawer !=null && mActionModeClosedByDrawer) { + for (int i = 0; i< mSelectionWhenActionModeClosedByDrawer.size(); i++) { + if (mSelectionWhenActionModeClosedByDrawer.valueAt(i)) { + getListView().setItemChecked( + mSelectionWhenActionModeClosedByDrawer.keyAt(i), + true + ); + } } + } + mSelectionWhenActionModeClosedByDrawer = null; + } - // hide FAB in multi selection mode - setFabEnabled(false); + /** + * If the action mode is active when the navigation drawer starts to move, the action + * mode is closed and the selection stored to be recovered when the drawer is closed. + * + * @param newState One of STATE_IDLE, STATE_DRAGGING or STATE_SETTLING. + */ + @Override + public void onDrawerStateChanged(int newState) { + if (DrawerLayout.STATE_DRAGGING == newState && mActiveActionMode != null) { + mSelectionWhenActionModeClosedByDrawer = getListView().getCheckedItemPositions().clone(); + mActiveActionMode.finish(); + mActionModeClosedByDrawer = true; + } + } - return true; + + /** + * Update action mode bar when an item is selected / unselected in the list + */ + @Override + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { + getListView().invalidateViews(); + mode.invalidate(); + } + + /** + * Load menu and customize UI when action mode is started. + */ + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + mActiveActionMode = mode; + + MenuInflater inflater = getActivity().getMenuInflater(); + inflater.inflate(R.menu.file_actions_menu, menu); + mode.invalidate(); + + //set gray color + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Window w = getActivity().getWindow(); + mStatusBarColor = w.getStatusBarColor(); + w.setStatusBarColor(mStatusBarColorActionMode); } - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - List checkedFiles = mAdapter.getCheckedItems(getListView()); - final int checkedCount = checkedFiles.size(); - String title = getResources().getQuantityString( - R.plurals.items_selected_count, - checkedCount, - checkedCount - ); - mode.setTitle(title); - FileMenuFilter mf = new FileMenuFilter( - checkedFiles, - ((FileActivity) getActivity()).getAccount(), - mContainerActivity, - getActivity() - ); - mf.filter(menu); - return true; + // hide FAB in multi selection mode + setFabEnabled(false); + + return true; + } + + /** + * Updates available action in menu depending on current selection. + */ + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + List checkedFiles = mAdapter.getCheckedItems(getListView()); + final int checkedCount = checkedFiles.size(); + String title = getResources().getQuantityString( + R.plurals.items_selected_count, + checkedCount, + checkedCount + ); + mode.setTitle(title); + FileMenuFilter mf = new FileMenuFilter( + checkedFiles, + ((FileActivity) getActivity()).getAccount(), + mContainerActivity, + getActivity() + ); + mf.filter(menu); + return true; + } + + /** + * Starts the corresponding action when a menu item is tapped by the user. + */ + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + return onFileActionChosen(item.getItemId()); + } + + /** + * Restores UI. + */ + @Override + public void onDestroyActionMode(ActionMode mode) { + mActiveActionMode = null; + + // reset to previous color + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getActivity().getWindow().setStatusBarColor(mStatusBarColor); } - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - return onFileActionChosen(item.getItemId()); + // show FAB on multi selection mode exit + if(!mHideFab) { + setFabEnabled(true); } + } - @Override - public void onDestroyActionMode(ActionMode mode) { - mActiveActionMode = null; - // reset to previous color - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - getActivity().getWindow().setStatusBarColor(mStatusBarColor); - } + public void storeStateIn(Bundle outState) { + outState.putBoolean(KEY_ACTION_MODE_CLOSED_BY_DRAWER, mActionModeClosedByDrawer); + if (mSelectionWhenActionModeClosedByDrawer != null) { + SparseBooleanArrayParcelable sbap = new SparseBooleanArrayParcelable( + mSelectionWhenActionModeClosedByDrawer + ); + outState.putParcelable(KEY_SELECTION_WHEN_CLOSED_BY_DRAWER, sbap); + } + } - // show FAB on multi selection mode exit - if(!mHideFab) { - setFabEnabled(true); - } + public void loadStateFrom(Bundle savedInstanceState) { + mActionModeClosedByDrawer = savedInstanceState.getBoolean( + KEY_ACTION_MODE_CLOSED_BY_DRAWER, + mActionModeClosedByDrawer + ); + SparseBooleanArrayParcelable sbap = savedInstanceState.getParcelable( + KEY_SELECTION_WHEN_CLOSED_BY_DRAWER + ); + if (sbap != null) { + mSelectionWhenActionModeClosedByDrawer = sbap.getSparseBooleanArray(); } - }); + } + } + + /** + * Init listener that will handle interactions in multiple selection mode. + */ + private void setChoiceModeAsMultipleModal(Bundle savedInstanceState) { + setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); + mMultiChoiceModeListener = new MultiChoiceModeListener(); + if (savedInstanceState != null) { + mMultiChoiceModeListener.loadStateFrom(savedInstanceState); + } + setMultiChoiceModeListener(mMultiChoiceModeListener); + ((FileActivity)getActivity()).addDrawerListener(mMultiChoiceModeListener); } /** @@ -441,6 +561,7 @@ public void onDestroyActionMode(ActionMode mode) { public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelable(KEY_FILE, mFile); + mMultiChoiceModeListener.storeStateIn(outState); } @Override diff --git a/src/com/owncloud/android/ui/helpers/SparseBooleanArrayParcelable.java b/src/com/owncloud/android/ui/helpers/SparseBooleanArrayParcelable.java new file mode 100644 index 00000000000..c4d4fa7a763 --- /dev/null +++ b/src/com/owncloud/android/ui/helpers/SparseBooleanArrayParcelable.java @@ -0,0 +1,91 @@ +/** + * ownCloud Android client application + * + * @author David A. Velasco + * Copyright (C) 2016 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android.ui.helpers; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.SparseBooleanArray; + +/** + * Wraps a SparseBooleanArrayParcelable to allow its serialization and desearialization + * through {@link Parcelable} interface. + */ +public class SparseBooleanArrayParcelable implements Parcelable { + + public static Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public SparseBooleanArrayParcelable createFromParcel(Parcel source) { + // read size of array from source + int size = source.readInt(); + + // then pairs of (key, value)s, in the object to wrap + SparseBooleanArray sba = new SparseBooleanArray(); + int key; + boolean value; + for (int i = 0; i < size; i++) { + key = source.readInt(); + value = (source.readInt() != 0); + sba.put(key, value); + } + + // wrap SparseBooleanArray + return new SparseBooleanArrayParcelable(sba); + } + + @Override + public SparseBooleanArrayParcelable[] newArray(int size) { + return new SparseBooleanArrayParcelable[size]; + } + }; + + private final SparseBooleanArray mSba; + + public SparseBooleanArrayParcelable(SparseBooleanArray sba) { + if (sba == null) { + throw new IllegalArgumentException("Cannot wrap a null SparseBooleanArray"); + } + mSba = sba; + } + + public SparseBooleanArray getSparseBooleanArray() { + return mSba; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + // first, size of the array + dest.writeInt(mSba.size()); + + // then, pairs of (key, value) + for (int i = 0; i < mSba.size(); i++) { + dest.writeInt(mSba.keyAt(i)); + dest.writeInt(mSba.valueAt(i) ? 1 : 0); + } + + } +} \ No newline at end of file From f435822270a24b3b0971fc87a04502b5d4b3f6ae Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Mon, 10 Oct 2016 12:49:10 +0200 Subject: [PATCH 80/84] Fix exit of the app after not creating account from 'Add account' in navigation drawer --- .../android/ui/activity/BaseActivity.java | 24 +++++++++++++++---- .../android/ui/activity/DrawerActivity.java | 2 +- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/BaseActivity.java b/src/com/owncloud/android/ui/activity/BaseActivity.java index 74128a1047b..61e64b2354b 100644 --- a/src/com/owncloud/android/ui/activity/BaseActivity.java +++ b/src/com/owncloud/android/ui/activity/BaseActivity.java @@ -116,7 +116,7 @@ protected void swapToDefaultAccount() { Account newAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()); if (newAccount == null) { /// no account available: force account creation - createAccount(); + createAccount(true); mRedirectingToSetupAccount = true; mAccountWasSet = false; mAccountWasRestored = false; @@ -129,16 +129,19 @@ protected void swapToDefaultAccount() { } /** - * Launches the account creation activity. To use when no ownCloud account is available. + * Launches the account creation activity. + * + * @param mandatoryCreation When 'true', if an account is not created by the user, the app will be closed. + * To use when no ownCloud account is available. */ - protected void createAccount() { + protected void createAccount(boolean mandatoryCreation) { AccountManager am = AccountManager.get(getApplicationContext()); am.addAccount(MainApp.getAccountType(), null, null, null, this, - new AccountCreationCallback(), + new AccountCreationCallback(mandatoryCreation), new Handler()); } @@ -216,6 +219,17 @@ protected void onAccountCreationSuccessful(AccountManagerFuture future) */ public class AccountCreationCallback implements AccountManagerCallback { + boolean mMandatoryCreation; + + /** + * Constuctor + * + * @param mandatoryCreation When 'true', if an account was not created, the app is closed. + */ + public AccountCreationCallback(boolean mandatoryCreation) { + mMandatoryCreation = mandatoryCreation; + } + @Override public void run(AccountManagerFuture future) { BaseActivity.this.mRedirectingToSetupAccount = false; @@ -242,7 +256,7 @@ public void run(AccountManagerFuture future) { } else { Log_OC.e(TAG, "Account creation callback with null bundle"); } - if (!accountWasSet) { + if (mMandatoryCreation && !accountWasSet) { moveTaskToBack(true); } } diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index e1329c82e36..d25ca7877f8 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -232,7 +232,7 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { startActivity(settingsIntent); break; case R.id.drawer_menu_account_add: - createAccount(); + createAccount(false); break; case R.id.drawer_menu_account_manage: Intent manageAccountsIntent = new Intent(getApplicationContext(), From 068d2a803213e47020016ddb65b535424af1946f Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Tue, 11 Oct 2016 10:17:05 +0200 Subject: [PATCH 81/84] Switch current account on click in account manager --- .../ui/activity/ManageAccountsActivity.java | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java index b5de6474dae..7d5c03a0e3c 100644 --- a/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/src/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -32,6 +32,8 @@ import android.os.Handler; import android.os.IBinder; import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; import android.widget.ListView; import com.owncloud.android.MainApp; @@ -67,7 +69,7 @@ public class ManageAccountsActivity extends FileActivity private ListView mListView; private final Handler mHandler = new Handler(); - private String mAccountName; + private String mAccountBeingRemoved; private AccountListAdapter mAccountListAdapter; protected FileUploader.FileUploaderBinder mUploaderBinder = null; protected FileDownloader.FileDownloaderBinder mDownloaderBinder = null; @@ -95,6 +97,14 @@ protected void onCreate(Bundle savedInstanceState) { onAccountSet(false); initializeComponentGetters(); + + // added click listener to switch account + mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + switchAccount(position); + } + }); } @Override @@ -201,7 +211,7 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void removeAccount(Account account) { - mAccountName = account.name; + mAccountBeingRemoved = account.name; RemoveAccountDialogFragment dialog = RemoveAccountDialogFragment.newInstance( account ); @@ -260,7 +270,7 @@ public void run() { @Override public void run(AccountManagerFuture future) { if (future != null && future.isDone()) { - Account account = new Account(mAccountName, MainApp.getAccountType()); + Account account = new Account(mAccountBeingRemoved, MainApp.getAccountType()); if (!AccountUtils.exists(account, MainApp.getAppContext())) { // Cancel transfers of the removed account if (mUploaderBinder != null) { @@ -298,6 +308,34 @@ public void run(AccountManagerFuture future) { } } + /** + * Switch current account to that contained in the received position of the list adapter. + * + * @param position A position of the account adapter containing an account. + */ + private void switchAccount(int position) { + Account clickedAccount = mAccountListAdapter.getItem(position).getAccount(); + if (getAccount().name.equals(clickedAccount.name)) { + // current account selected, just go back + finish(); + } else { + // restart list of files with new account + AccountUtils.setCurrentOwnCloudAccount( + ManageAccountsActivity.this, + clickedAccount.name + ); + Intent i = new Intent( + ManageAccountsActivity.this, + FileDisplayActivity.class + ); + i.putExtra(FileActivity.EXTRA_ACCOUNT, clickedAccount); + i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(i); + } + + } + + @Override protected void onDestroy() { if (mDownloadServiceConnection != null) { From 7c8a2e9389ab8663267c021090fd7bed2dce7b1f Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Tue, 11 Oct 2016 10:18:09 +0200 Subject: [PATCH 82/84] Show a check mark on the current account in the account manager --- res/drawable/ic_current.png | Bin 0 -> 1767 bytes res/drawable/ic_current_white.png | Bin 0 -> 1194 bytes res/layout/account_item.xml | 32 ++++++++---------- .../ui/activity/ManageAccountsActivity.java | 20 ++++++++--- .../ui/adapter/AccountListAdapter.java | 19 +++++++++-- 5 files changed, 47 insertions(+), 24 deletions(-) create mode 100644 res/drawable/ic_current.png create mode 100644 res/drawable/ic_current_white.png diff --git a/res/drawable/ic_current.png b/res/drawable/ic_current.png new file mode 100644 index 0000000000000000000000000000000000000000..a0d859ac3176a959351b1fe7a9a3d9d973bd73f8 GIT binary patch literal 1767 zcmVcf&V&E)+eo#_}j|ML` zT~BC&@*l4#Wr1A)ssN;z{PRNqejz~ze4d6g2}Mv|k@H!Ccn6>jF|h&R6bNto{&bzT zG(q`A6@`NE88J4;BcL+jCmGV~UGDl0%Mp~f(z%_84*|%CbK)5Q@oRo}$lp+bKJv&3aK0Op;J)YT+4GjP5A=E6 zrpKT~%QrnFY|147Sr#W@0KjaO?{C_emun5$nk}Z-#ODb>j#m*V0@Ir+ZF0hpJ~h>1 zi;62Z0{o%HM{WWv2)9#}5{pCL`zG4HDgn7*`?Qh$@RMC@#yB*w$sFUW$owSSk;;*85e; zk{V`ItK}Y9g!4K*O+AJbBnjKL8B7iU$U;te$+*P`TP$lUHFAoado++Ul7LujF=Vj^ zmz89ur>5TkV4H!;8IPeMlIHZf>u>2&P&&H?lG*K zdx1Fey{cKvq5TnHm(T602{bqStq3jw;5C7qwrRE1eU}f1ku;y7QJ%Z~t*!%O{|7xT zpCV8WpgkIL1clAcR%#4oP76T{st&kaA5Z)|=yCZJmW-0rCMaCq?QROhgq#tN86rr_ zU{b_6r(kQnA1pQj@aqVcT@p!^s^ml&kPX~pL<~-?Al{!}Tu~FTCH!`R8x?bhvX3dpj}8FyQaCxN7kx`#XFuYO^cJQTVC zz-~yigj%25Rb%AXSSe=|A$uq#C(<&Rhzbf`{P8w`^TuVXt|Y6}ISf9hwuVTxP$cGK zQJ!2iaK|SgPgFV`Gg>HOC?)k90K?G*K=r^KCtKaSMONgaY7JE9#s2MSyPcK0WIYh| zQkSyq+$DGV`@Cl&4_V;d7?T4g91pZM95Jk*Q4#Jp$?KEaEgrTSH_Y+gRBfi5SsmzW z@5;(uyb+*9CR|flwxf{xd%aC@wAI)HJK5dR`2JMwG!4p2B-aWSjph_(sLn5PhDQG8 zxR3*YiJ&QYXkH(*aAn2UAnd@*z!86o>y>DSJMM3&$a?|w&j506+L-s5F&=b(XY*3s zHDcaA65A3M%%JUhmIUisquAfqaWy-)@Bsi%B_==gNuZ_SOI`gm-5tBZ@NR&AO*m=@ zr|coyKHbL|IoasE{V4#e6M`JzTsSqoVzhsWX;lyaT)%Q=P!{3M0Ne?noMxGMs}XVx z69fRAo~E85lAZ=|!ou_%zYD3<=speIL3$!?^|lYsnU()NQpo|LCoJZx!}d_h%l=Ds zp(yQX4rc0xI@z?V>hroY=PdpTHYtrp_K(GS%elftUqHxP1Fa1gObqbmBe|(pBa+*~ zH6r<90Ue0&YKjE_vH|3bbtnP+14eheUrmuj5=kVHL=uUM{{XnB7L8>AY^VSL002ov JPDHLkV1ky=F-QOa literal 0 HcmV?d00001 diff --git a/res/drawable/ic_current_white.png b/res/drawable/ic_current_white.png new file mode 100644 index 0000000000000000000000000000000000000000..de2fc111b620d5399bca6f7d1fef253c55453db1 GIT binary patch literal 1194 zcmV;b1XcTqP)!;!YD|P}Ia~P?GxC5Er7+R%sCg@kvr!MgK1DTytaEd7L?S=FD~en=bC0^PTzi z`@T8n%y$NA)v8siR;{YaiZ&>z4_F1<46FdI1ug^50{WuMW`W;ZN{gNJ(G$v_A?wXPGuB0uJF6sp^Ly}&X^oRUwPSR_VZm%MkwUYL!IK3xns0v`N zkhDkTX^*7kp~4xJbW-K(S4kT}0@Ekyd4;E4lFlskF)yhfcoVoK(5U|exW~*+6b?wz zCBQqtje)1e1HfHocC0W!k}d}J16Nn%!TLF{+RT2++HjHMO!$=w!2!4)c-18*Epjln z3s_UZdFwFnY*#}}(ner+#V7a=nAywT0ZFslfaKJfd8dz^;Q!&@f#{Bk3Uhf%@^jE;1vovA+&4fNqMN1}U58B{3 zne4VEfMGM6j)Md2Na*J@;Ql-w^g)20GbQQDgmE`A^f%@TXR)L|doZl#+uJ_JCB$w8 zP7f%orhs(`Vbw?xR{zx*v|dAH2DCD;oHJx*lLxFZ>j!CvLMT9d+USpZ?xsXJT%oh@lLhtXz^Cup-k zI5W;=G9EU}8V)v#^c?pMvuLmnc^E)5P|s;=a~?qbo+?zEfx?-Sv?TA0datL!Gy)2% ze~UhSEIHPA98fb5&uMFwuXWkJZa^v-PO9W=DFKZDWBAkd=G=9W=Db1$UUnn0i#(;f-~(Xn%Nt`a}@^kq?zr_bk7b? zv_4t|w!GiLh{9t!J8@qXB@cG%x1DSblS8xvy;HRz-JYJa{#!{1x~KF z8fNxAa2xP;z;NE8^-lj-X(QTKGd5URDCQw z!_H(SKP(W+rztK6&Zl+Iq`jd01^ABt!>1`~)v8siR;{k+ACT_T7=03YJ^%m!07*qo IM6N<$f_0uGDgXcg literal 0 HcmV?d00001 diff --git a/res/layout/account_item.xml b/res/layout/account_item.xml index 3b0fa03411d..fb9f85b57ff 100644 --- a/res/layout/account_item.xml +++ b/res/layout/account_item.xml @@ -31,23 +31,23 @@ android:layout_centerInParent="true" android:layout_marginLeft="@dimen/standard_margin" android:layout_marginStart="@dimen/standard_margin" - android:src="@drawable/ic_menu_archive"/> - - - + + mOriginalAccounts; String mOriginalCurrentAccount; + private Drawable mTintedCheck; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mTintedCheck = ContextCompat.getDrawable(this, R.drawable.ic_current_white); + mTintedCheck = DrawableCompat.wrap(mTintedCheck); + int tint = ContextCompat.getColor(this, R.color.actionbar_start_color); + DrawableCompat.setTint(mTintedCheck, tint); + setContentView(R.layout.accounts_layout); mListView = (ListView) findViewById(R.id.account_list); @@ -110,7 +119,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) @Override protected void onStart() { super.onStart(); - mAccountListAdapter = new AccountListAdapter(this, getAccountListItems()); + mAccountListAdapter = new AccountListAdapter(this, getAccountListItems(), mTintedCheck); mListView.setAdapter(mAccountListAdapter); } @@ -243,8 +252,11 @@ public void run(AccountManagerFuture future) { Bundle result = future.getResult(); String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name); - mAccountListAdapter = new AccountListAdapter(ManageAccountsActivity - .this, getAccountListItems()); + mAccountListAdapter = new AccountListAdapter( + ManageAccountsActivity.this, + getAccountListItems(), + mTintedCheck + ); mListView.setAdapter(mAccountListAdapter); runOnUiThread(new Runnable() { @Override @@ -281,7 +293,7 @@ public void run(AccountManagerFuture future) { } } - mAccountListAdapter = new AccountListAdapter(this, getAccountListItems()); + mAccountListAdapter = new AccountListAdapter(this, getAccountListItems(), mTintedCheck); mListView.setAdapter(mAccountListAdapter); AccountManager am = AccountManager.get(this); diff --git a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java index de8fde05b3a..b910fdf9707 100644 --- a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java @@ -20,6 +20,7 @@ package com.owncloud.android.ui.adapter; import android.accounts.Account; +import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -45,13 +46,15 @@ public class AccountListAdapter extends ArrayAdapter { private final BaseActivity mContext; private List mValues; private AccountListAdapterListener mListener; + private Drawable mTintedCheck; - public AccountListAdapter(BaseActivity context, List values) { + public AccountListAdapter(BaseActivity context, List values, Drawable tintedCheck) { super(context, -1, values); this.mContext = context; this.mValues = values; this.mListener = (AccountListAdapterListener) context; this.mAccountAvatarRadiusDimension = context.getResources().getDimension(R.dimen.list_item_avatar_icon_radius); + this.mTintedCheck = tintedCheck; } @Override @@ -63,9 +66,11 @@ public View getView(final int position, View convertView, ViewGroup parent) { convertView = inflater.inflate(R.layout.account_item, parent, false); viewHolder = new AccountViewHolderItem(); + viewHolder.imageViewItem = (ImageView) convertView.findViewById(R.id.icon); + viewHolder.checkViewItem = (ImageView) convertView.findViewById(R.id.ticker); + viewHolder.checkViewItem.setImageDrawable(mTintedCheck); viewHolder.nameViewItem = (TextView) convertView.findViewById(R.id.name); viewHolder.accountViewItem = (TextView) convertView.findViewById(R.id.account); - viewHolder.imageViewItem = (ImageView) convertView.findViewById(R.id.icon); viewHolder.passwordButtonItem = (ImageView) convertView.findViewById(R.id.passwordButton); viewHolder.removeButtonItem = (ImageView) convertView.findViewById(R.id.removeButton); @@ -108,6 +113,12 @@ public View getView(final int position, View convertView, ViewGroup parent) { viewHolder.imageViewItem.setImageResource(R.drawable.ic_user); } + if (AccountUtils.getCurrentOwnCloudAccount(getContext()).name.equals(account.name)) { + viewHolder.checkViewItem.setVisibility(View.VISIBLE); + } else { + viewHolder.checkViewItem.setVisibility(View.INVISIBLE); + } + /// bind listener to change password viewHolder.passwordButtonItem.setOnClickListener(new View.OnClickListener() { @Override @@ -160,9 +171,11 @@ public interface AccountListAdapterListener { * Account ViewHolderItem to get smooth scrolling. */ static class AccountViewHolderItem { + ImageView imageViewItem; + ImageView checkViewItem; + TextView nameViewItem; TextView accountViewItem; - ImageView imageViewItem; ImageView passwordButtonItem; ImageView removeButtonItem; From 328f4de414f6664c107a35b4746625787ba63fe2 Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Tue, 11 Oct 2016 14:00:16 +0200 Subject: [PATCH 83/84] Fix duplicated avatars for different accounts with same username, and some clean-up --- .../datamodel/ThumbnailsCacheManager.java | 152 ++++-------------- .../android/ui/activity/DrawerActivity.java | 8 +- .../ui/adapter/AccountListAdapter.java | 2 +- .../owncloud/android/utils/DisplayUtils.java | 7 +- 4 files changed, 42 insertions(+), 127 deletions(-) diff --git a/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index 899f6510758..3b194786d58 100644 --- a/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -32,6 +32,7 @@ import android.media.ThumbnailUtils; import android.net.Uri; import android.os.AsyncTask; +import android.support.v4.content.ContextCompat; import android.view.MenuItem; import android.widget.ImageView; @@ -149,7 +150,7 @@ public static class ThumbnailGenerationTask extends AsyncTask(imageView); + mImageViewReference = new WeakReference<>(imageView); if (storageManager == null) throw new IllegalArgumentException("storageManager must not be NULL"); mStorageManager = storageManager; @@ -158,7 +159,7 @@ public ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager stora public ThumbnailGenerationTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can be garbage collected - mImageViewReference = new WeakReference(imageView); + mImageViewReference = new WeakReference<>(imageView); } @Override @@ -328,8 +329,7 @@ private Bitmap handlePNG(Bitmap bitmap, int px){ Bitmap.Config.ARGB_8888); Canvas c = new Canvas(resultBitmap); - c.drawColor(MainApp.getAppContext().getResources(). - getColor(R.color.background_color)); + c.drawColor(ContextCompat.getColor(MainApp.getAppContext(), R.color.background_color)); c.drawBitmap(bitmap, 0, 0, null); return resultBitmap; @@ -363,56 +363,40 @@ private Bitmap doFileInBackground() { public static class AvatarGenerationTask extends AsyncTask { private final WeakReference mImageViewReference; private final WeakReference mMenuItemReference; - private static Account mAccount; - private Object mUsername; - private FileDataStorageManager mStorageManager; - + private Account mAccount; + private String mUsername; + private OwnCloudClient mClient; - public AvatarGenerationTask(ImageView imageView, FileDataStorageManager storageManager, - Account account) { - // Use a WeakReference to ensure the ImageView can be garbage collected + public AvatarGenerationTask(ImageView imageView, Account account) { + if (account == null) { + throw new IllegalArgumentException("Received NULL account"); + } mMenuItemReference = null; - mImageViewReference = new WeakReference(imageView); - if (storageManager == null) - throw new IllegalArgumentException("storageManager must not be NULL"); - mStorageManager = storageManager; + mImageViewReference = new WeakReference<>(imageView); mAccount = account; } - public AvatarGenerationTask(MenuItem menuItem, FileDataStorageManager storageManager, - Account account) { - // Use a WeakReference to ensure the ImageView can be garbage collected + public AvatarGenerationTask(MenuItem menuItem, Account account) { + if (account == null) { + throw new IllegalArgumentException("Received NULL account"); + } mImageViewReference = null; - mMenuItemReference = new WeakReference(menuItem); - if (storageManager == null) - throw new IllegalArgumentException("storageManager must not be NULL"); - mStorageManager = storageManager; + mMenuItemReference = new WeakReference<>(menuItem); mAccount = account; } - public AvatarGenerationTask(ImageView imageView) { - // Use a WeakReference to ensure the ImageView can be garbage collected - mMenuItemReference = null; - mImageViewReference = new WeakReference(imageView); - } - @Override protected Bitmap doInBackground(Object... params) { Bitmap thumbnail = null; try { - if (mAccount != null) { - OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, - MainApp.getAppContext()); - mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, MainApp.getAppContext()); - } - - mUsername = params[0]; + OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, + MainApp.getAppContext()); + mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). + getClientFor(ocAccount, MainApp.getAppContext()); - if (mUsername instanceof String) { - thumbnail = doAvatarInBackground(); - } + mUsername = mAccount.name; + thumbnail = doAvatarInBackground(); } catch(Throwable t){ // the app should never break due to a problem with avatars @@ -431,52 +415,23 @@ protected void onPostExecute(Bitmap bitmap) { ImageView imageView = mImageViewReference.get(); AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(imageView); if (this == avatarWorkerTask) { - String tagId = ""; - if (mUsername instanceof String) { - tagId = (String) mUsername; - if (String.valueOf(imageView.getTag()).equals(tagId)) { - imageView.setImageBitmap(bitmap); - } + if (String.valueOf(imageView.getTag()).equals(mUsername)) { + imageView.setImageBitmap(bitmap); } } - } else { + } else if (mMenuItemReference != null) { MenuItem menuItem = mMenuItemReference.get(); AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(menuItem); if (this == avatarWorkerTask) { - String tagId = ""; - if (mUsername instanceof String) { - tagId = (String) mUsername; - if (String.valueOf(menuItem.getTitle()).equals(tagId)) { - menuItem.setIcon(new BitmapDrawable(MainApp.getAppContext().getResources(), - bitmap)); - } + if (String.valueOf(menuItem.getTitle()).equals(mUsername)) { + menuItem.setIcon(new BitmapDrawable(MainApp.getAppContext().getResources(), + bitmap)); } } } } } - /** - * Add thumbnail to cache - * @param imageKey: thumb key - * @param bitmap: image for extracting thumbnail - * @param path: image path - * @param px: thumbnail dp - * @return Bitmap - */ - private Bitmap addThumbnailToCache(String imageKey, Bitmap bitmap, String path, int px){ - - Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px); - - // Rotate image, obeying exif tag - thumbnail = BitmapUtils.rotateImage(thumbnail,path); - - // Add thumbnail to cache - addBitmapToCache(imageKey, thumbnail); - - return thumbnail; - } - /** * Converts size of file icon from dp to pixel * @return int @@ -488,9 +443,8 @@ private int getAvatarDimension(){ } private Bitmap doAvatarInBackground() { - String username = (String) mUsername; - final String imageKey = "a_" + username; + final String imageKey = "a_" + mUsername; // Check disk cache in background thread Bitmap avatar = getBitmapFromDiskCache(imageKey); @@ -507,7 +461,7 @@ private Bitmap doAvatarInBackground() { GetMethod get = null; try { String uri = mClient.getBaseUri() + "" + - "/index.php/avatar/" + AccountUtils.getUsernameOfAccount(username) + "/" + px; + "/index.php/avatar/" + AccountUtils.getUsernameOfAccount(mUsername) + "/" + px; Log_OC.d("Avatar", "URI: " + uri); get = new GetMethod(uri); int status = mClient.executeMethod(get); @@ -538,42 +492,6 @@ private Bitmap doAvatarInBackground() { return avatar; } - private Bitmap handlePNG(Bitmap bitmap, int px){ - Bitmap resultBitmap = Bitmap.createBitmap(px, - px, - Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(resultBitmap); - - c.drawColor(MainApp.getAppContext().getResources(). - getColor(R.color.background_color)); - c.drawBitmap(bitmap, 0, 0, null); - - return resultBitmap; - } - - private Bitmap doFileInBackground() { - File file = (File) mUsername; - - final String imageKey = String.valueOf(file.hashCode()); - - // Check disk cache in background thread - Bitmap thumbnail = getBitmapFromDiskCache(imageKey); - - // Not found in disk cache - if (thumbnail == null) { - - int px = getAvatarDimension(); - - Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile( - file.getAbsolutePath(), px, px); - - if (bitmap != null) { - thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px); - } - } - return thumbnail; - } - } public static boolean cancelPotentialThumbnailWork(Object file, ImageView imageView) { @@ -633,7 +551,7 @@ public static boolean cancelPotentialAvatarWork(Object file, MenuItem menuItem) return true; } - public static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { + private static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { if (imageView != null) { final Drawable drawable = imageView.getDrawable(); if (drawable instanceof AsyncThumbnailDrawable) { @@ -644,21 +562,21 @@ public static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) { return null; } - public static AvatarGenerationTask getAvatarWorkerTask(ImageView imageView) { + private static AvatarGenerationTask getAvatarWorkerTask(ImageView imageView) { if (imageView != null) { return getAvatarWorkerTask(imageView.getDrawable()); } return null; } - public static AvatarGenerationTask getAvatarWorkerTask(MenuItem menuItem) { + private static AvatarGenerationTask getAvatarWorkerTask(MenuItem menuItem) { if (menuItem != null) { return getAvatarWorkerTask(menuItem.getIcon()); } return null; } - public static AvatarGenerationTask getAvatarWorkerTask(Drawable drawable) { + private static AvatarGenerationTask getAvatarWorkerTask(Drawable drawable) { if (drawable instanceof AsyncAvatarDrawable) { final AsyncAvatarDrawable asyncDrawable = (AsyncAvatarDrawable) drawable; return asyncDrawable.getAvatarWorkerTask(); diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index d25ca7877f8..70bd167dec8 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -345,7 +345,7 @@ public void updateAccountList() { if (mAvatars[1] != null) { DisplayUtils.setAvatar(mAvatars[1], (ImageView) findNavigationViewChildById(R.id.drawer_account_end), - mOtherAccountAvatarRadiusDimension, getResources(), getStorageManager()); + mOtherAccountAvatarRadiusDimension, getResources()); mAccountEndAccountAvatar.setVisibility(View.VISIBLE); } else { mAccountEndAccountAvatar.setVisibility(View.GONE); @@ -355,7 +355,7 @@ public void updateAccountList() { if (mAvatars[2] != null) { DisplayUtils.setAvatar(mAvatars[2], (ImageView) findNavigationViewChildById(R.id.drawer_account_middle), - mOtherAccountAvatarRadiusDimension, getResources(), getStorageManager()); + mOtherAccountAvatarRadiusDimension, getResources()); mAccountMiddleAccountAvatar.setVisibility(View.VISIBLE); } else { mAccountMiddleAccountAvatar.setVisibility(View.GONE); @@ -463,7 +463,7 @@ protected void setAccountInDrawer(Account account) { } DisplayUtils.setAvatar(account, (ImageView) findNavigationViewChildById(R.id.drawer_current_account), - mCurrentAccountAvatarRadiusDimension, getResources(), getStorageManager()); + mCurrentAccountAvatarRadiusDimension, getResources()); } } @@ -488,7 +488,7 @@ private void setAvatar(Account account, MenuItem menuItem) { if (ThumbnailsCacheManager.cancelPotentialAvatarWork(account.name, menuItem)) { final ThumbnailsCacheManager.AvatarGenerationTask task = new ThumbnailsCacheManager.AvatarGenerationTask( - menuItem, getStorageManager(), account + menuItem, account ); if (thumbnail == null) { try { diff --git a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java index b910fdf9707..6ea2114302b 100644 --- a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java @@ -106,7 +106,7 @@ public View getView(final int position, View convertView, ViewGroup parent) { try { DisplayUtils.setAvatar(account, viewHolder.imageViewItem, mAccountAvatarRadiusDimension, - mContext.getResources(), mContext.getStorageManager()); + mContext.getResources()); } catch (Exception e) { Log_OC.e(TAG, "Error calculating RGB value for account list item.", e); // use user icon as a fallback diff --git a/src/com/owncloud/android/utils/DisplayUtils.java b/src/com/owncloud/android/utils/DisplayUtils.java index a80dd00f1b1..8a01068f94f 100644 --- a/src/com/owncloud/android/utils/DisplayUtils.java +++ b/src/com/owncloud/android/utils/DisplayUtils.java @@ -41,7 +41,6 @@ import com.owncloud.android.MainApp; import com.owncloud.android.R; -import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.lib.common.utils.Log_OC; @@ -308,10 +307,8 @@ public static void colorSnackbar(Context context, Snackbar snackbar) { * @param userIcon the image view to set the avatar on * @param avatarRadius the avatar radius * @param resources reference for density information - * @param storageManager reference for caching purposes */ - public static void setAvatar(Account account, ImageView userIcon, float avatarRadius, Resources resources, - FileDataStorageManager storageManager) { + public static void setAvatar(Account account, ImageView userIcon, float avatarRadius, Resources resources) { if (account != null) { userIcon.setContentDescription(account.name); @@ -329,7 +326,7 @@ public static void setAvatar(Account account, ImageView userIcon, float avatarRa // generate new avatar if (ThumbnailsCacheManager.cancelPotentialAvatarWork(account.name, userIcon)) { final ThumbnailsCacheManager.AvatarGenerationTask task = - new ThumbnailsCacheManager.AvatarGenerationTask(userIcon, storageManager, account); + new ThumbnailsCacheManager.AvatarGenerationTask(userIcon, account); if (thumbnail == null) { try { userIcon.setImageDrawable( From 77483212ee8ee62b7b49ca42723de136fb0e1f24 Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Fri, 14 Oct 2016 14:15:29 +0200 Subject: [PATCH 84/84] Avatars: update when changed in server, full load in background, selective server fetches, refactoring and clean-up --- owncloud-android-library | 2 +- res/layout/account_item.xml | 3 +- .../datamodel/ThumbnailsCacheManager.java | 227 +++++++++++++----- .../android/datamodel/UserProfile.java | 93 +++++++ .../datamodel/UserProfilesRepository.java | 194 +++++++++++++++ src/com/owncloud/android/db/ProviderMeta.java | 10 +- .../operations/GetUserProfileOperation.java | 128 ++++++++-- .../operations/RefreshFolderOperation.java | 17 +- .../providers/FileContentProvider.java | 35 ++- .../android/ui/activity/DrawerActivity.java | 123 +++------- .../ui/activity/FileDisplayActivity.java | 3 +- .../ui/adapter/AccountListAdapter.java | 8 +- .../owncloud/android/utils/DisplayUtils.java | 68 +++--- 13 files changed, 674 insertions(+), 237 deletions(-) create mode 100644 src/com/owncloud/android/datamodel/UserProfile.java create mode 100644 src/com/owncloud/android/datamodel/UserProfilesRepository.java diff --git a/owncloud-android-library b/owncloud-android-library index dc14474c51d..a13b5ec17c9 160000 --- a/owncloud-android-library +++ b/owncloud-android-library @@ -1 +1 @@ -Subproject commit dc14474c51d19884629a190e24a8de42aba1a268 +Subproject commit a13b5ec17c9e9550e519a884fca0d428c4f96507 diff --git a/res/layout/account_item.xml b/res/layout/account_item.xml index fb9f85b57ff..f6a79fe5863 100644 --- a/res/layout/account_item.xml +++ b/res/layout/account_item.xml @@ -18,6 +18,7 @@ --> @@ -31,7 +32,7 @@ android:layout_centerInParent="true" android:layout_marginLeft="@dimen/standard_margin" android:layout_marginStart="@dimen/standard_margin" - android:src="@drawable/ic_menu_archive" + android:src="@drawable/ic_account_plus" /> { @Override @@ -122,6 +123,14 @@ public static void addBitmapToCache(String key, Bitmap bitmap) { } } + public static void removeBitmapFromCache(String key) { + synchronized (mThumbnailsDiskCacheLock) { + if (mThumbnailCache != null) { + mThumbnailCache.removeKey(key); + } + } + } + public static Bitmap getBitmapFromDiskCache(String key) { synchronized (mThumbnailsDiskCacheLock) { @@ -360,34 +369,72 @@ private Bitmap doFileInBackground() { } - public static class AvatarGenerationTask extends AsyncTask { + /** + * Show the avatar corresponding to the received account in an {@link ImageView} ir {@link MenuItem}. + * + * The avatar is loaded if available in the cache and bound to the received UI element. The avatar is not + * fetched from the server if not available, unless the parameter 'fetchFromServer' is set to 'true'. + * + * If there is no avatar stored and cannot be fetched, a colored icon is generated with the first + * letter of the account username. + * + * If this is not possible either, a predefined user icon is bound instead. + */ + public static class GetAvatarTask extends AsyncTask { private final WeakReference mImageViewReference; private final WeakReference mMenuItemReference; private Account mAccount; + private float mDisplayRadius; + private boolean mFetchFromServer; + private String mUsername; private OwnCloudClient mClient; - public AvatarGenerationTask(ImageView imageView, Account account) { + /** + * Builds an instance to show the avatar corresponding to the received account in an {@link ImageView}. + * + * @param imageView The {@link ImageView} to bind the avatar to. + * @param account OC account which avatar will be shown. + * @param displayRadius The radius of the circle where the avatar will be clipped into. + * @param fetchFromServer When 'true', if there is no avatar stored in the cache, it's fetched from + * the server. When 'false', server is not accessed, the fallback avatar is + * generated instead. USE WITH CARE, probably to be removed in the future. + */ + public GetAvatarTask(ImageView imageView, Account account, float displayRadius, boolean fetchFromServer){ if (account == null) { throw new IllegalArgumentException("Received NULL account"); } mMenuItemReference = null; mImageViewReference = new WeakReference<>(imageView); mAccount = account; + mDisplayRadius = displayRadius; + mFetchFromServer = fetchFromServer; } - public AvatarGenerationTask(MenuItem menuItem, Account account) { + /** + * Builds an instance to show the avatar corresponding to the received account in an {@link MenuItem}. + * + * @param menuItem The {@ImageView} to bind the avatar to. + * @param account OC account which avatar will be shown. + * @param displayRadius The radius of the circle where the avatar will be clipped into. + * @param fetchFromServer When 'true', if there is no avatar stored in the cache, it's fetched from + * the server. When 'false', server is not accessed, the fallback avatar is + * generated instead. USE WITH CARE, probably to be removed in the future. + */ + public GetAvatarTask(MenuItem menuItem, Account account, float displayRadius, boolean fetchFromServer) { if (account == null) { throw new IllegalArgumentException("Received NULL account"); } mImageViewReference = null; mMenuItemReference = new WeakReference<>(menuItem); mAccount = account; + mDisplayRadius = displayRadius; + mFetchFromServer = fetchFromServer; } @Override - protected Bitmap doInBackground(Object... params) { - Bitmap thumbnail = null; + protected Drawable doInBackground(Object... params) { + Drawable thumbnail = null; try { OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, @@ -409,24 +456,30 @@ protected Bitmap doInBackground(Object... params) { return thumbnail; } - protected void onPostExecute(Bitmap bitmap) { - if (bitmap != null) { - if (mImageViewReference != null) { - ImageView imageView = mImageViewReference.get(); - AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(imageView); - if (this == avatarWorkerTask) { - if (String.valueOf(imageView.getTag()).equals(mUsername)) { - imageView.setImageBitmap(bitmap); - } + @Override + protected void onPostExecute(Drawable avatar) { + if (mImageViewReference != null) { + ImageView imageView = mImageViewReference.get(); + if (imageView != null) { + if (avatar != null) { + imageView.setImageDrawable(avatar); + } else { + // really needed? + imageView.setImageResource( + R.drawable.ic_account_circle + ); } - } else if (mMenuItemReference != null) { - MenuItem menuItem = mMenuItemReference.get(); - AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(menuItem); - if (this == avatarWorkerTask) { - if (String.valueOf(menuItem.getTitle()).equals(mUsername)) { - menuItem.setIcon(new BitmapDrawable(MainApp.getAppContext().getResources(), - bitmap)); - } + } + } else if (mMenuItemReference != null) { + MenuItem menuItem = mMenuItemReference.get(); + if (menuItem != null) { + if (avatar != null) { + menuItem.setIcon(avatar); + } else { + // really needed + menuItem.setIcon( + R.drawable.ic_account_circle + ); } } } @@ -442,56 +495,98 @@ private int getAvatarDimension(){ return Math.round(r.getDimension(R.dimen.file_avatar_size)); } - private Bitmap doAvatarInBackground() { + private Drawable doAvatarInBackground() { + + Drawable avatarDrawable = null; final String imageKey = "a_" + mUsername; // Check disk cache in background thread - Bitmap avatar = getBitmapFromDiskCache(imageKey); + Bitmap avatarBitmap = getBitmapFromDiskCache(imageKey); - // Not found in disk cache - if (avatar == null) { + if (avatarBitmap != null) { + avatarDrawable = BitmapUtils.bitmapToCircularBitmapDrawable( + MainApp.getAppContext().getResources(), + avatarBitmap + ); - int px = getAvatarDimension(); + } else { + // Not found in disk cache + if (mFetchFromServer) { + int px = getAvatarDimension(); - // Download avatar from server - OwnCloudVersion serverOCVersion = AccountUtils.getServerVersion(mAccount); - if (mClient != null && serverOCVersion != null) { - if (serverOCVersion.supportsRemoteThumbnails()) { - GetMethod get = null; - try { - String uri = mClient.getBaseUri() + "" + + // Download avatar from server + OwnCloudVersion serverOCVersion = AccountUtils.getServerVersion(mAccount); + if (mClient != null && serverOCVersion != null) { + if (serverOCVersion.supportsRemoteThumbnails()) { + GetMethod get = null; + try { + String uri = mClient.getBaseUri() + "" + "/index.php/avatar/" + AccountUtils.getUsernameOfAccount(mUsername) + "/" + px; - Log_OC.d("Avatar", "URI: " + uri); - get = new GetMethod(uri); - int status = mClient.executeMethod(get); - if (status == HttpStatus.SC_OK) { - InputStream inputStream = get.getResponseBodyAsStream(); - Bitmap bitmap = BitmapFactory.decodeStream(inputStream); - avatar = ThumbnailUtils.extractThumbnail(bitmap, px, px); - - // Add avatar to cache - if (avatar != null) { - addBitmapToCache(imageKey, avatar); + Log_OC.d("Avatar", "URI: " + uri); + get = new GetMethod(uri); + int status = mClient.executeMethod(get); + if (status == HttpStatus.SC_OK) { + InputStream inputStream = get.getResponseBodyAsStream(); + Bitmap bitmap = BitmapFactory.decodeStream(inputStream); + avatarBitmap = ThumbnailUtils.extractThumbnail(bitmap, px, px); + + // Add avatar to cache + if (avatarBitmap != null) { + addBitmapToCache(imageKey, avatarBitmap); + } + } else { + mClient.exhaustResponse(get.getResponseBodyAsStream()); + } + } catch (Exception e) { + Log_OC.e(TAG, "Error downloading avatar", e); + } finally { + if (get != null) { + get.releaseConnection(); } - } else { - mClient.exhaustResponse(get.getResponseBodyAsStream()); - } - } catch (Exception e) { - Log_OC.e(TAG, "Error downloading avatar", e); - } finally { - if (get != null) { - get.releaseConnection(); } + } else { + Log_OC.d(TAG, "Server too old"); } - } else { - Log_OC.d(TAG, "Server too old"); + } + } + if (avatarBitmap != null) { + avatarDrawable = BitmapUtils.bitmapToCircularBitmapDrawable( + MainApp.getAppContext().getResources(), + avatarBitmap + ); + + } else { + // generate placeholder from user name + try { + avatarDrawable = DefaultAvatarTextDrawable.createAvatar(mUsername, mDisplayRadius); + + } catch (Exception e) { + // nothing to do, return null to apply default icon + Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); } } } - return avatar; + return avatarDrawable; + } + + } + + public static String addAvatarToCache(String accountName, byte[] avatarData, int dimension) { + final String imageKey = "a_" + accountName; + + Bitmap bitmap = BitmapFactory.decodeByteArray(avatarData, 0, avatarData.length); + bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension); + // Add avatar to cache + if (bitmap != null) { + addBitmapToCache(imageKey, bitmap); } + return imageKey; + } + public static void removeAvatarFromCache(String accountName) { + final String imageKey = "a_" + accountName; + removeBitmapFromCache(imageKey); } public static boolean cancelPotentialThumbnailWork(Object file, ImageView imageView) { @@ -514,7 +609,7 @@ public static boolean cancelPotentialThumbnailWork(Object file, ImageView imageV } public static boolean cancelPotentialAvatarWork(Object file, ImageView imageView) { - final AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(imageView); + final GetAvatarTask avatarWorkerTask = getAvatarWorkerTask(imageView); if (avatarWorkerTask != null) { final Object usernameData = avatarWorkerTask.mUsername; @@ -533,7 +628,7 @@ public static boolean cancelPotentialAvatarWork(Object file, ImageView imageView } public static boolean cancelPotentialAvatarWork(Object file, MenuItem menuItem) { - final AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(menuItem); + final GetAvatarTask avatarWorkerTask = getAvatarWorkerTask(menuItem); if (avatarWorkerTask != null) { final Object usernameData = avatarWorkerTask.mUsername; @@ -562,21 +657,21 @@ private static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) return null; } - private static AvatarGenerationTask getAvatarWorkerTask(ImageView imageView) { + private static GetAvatarTask getAvatarWorkerTask(ImageView imageView) { if (imageView != null) { return getAvatarWorkerTask(imageView.getDrawable()); } return null; } - private static AvatarGenerationTask getAvatarWorkerTask(MenuItem menuItem) { + private static GetAvatarTask getAvatarWorkerTask(MenuItem menuItem) { if (menuItem != null) { return getAvatarWorkerTask(menuItem.getIcon()); } return null; } - private static AvatarGenerationTask getAvatarWorkerTask(Drawable drawable) { + private static GetAvatarTask getAvatarWorkerTask(Drawable drawable) { if (drawable instanceof AsyncAvatarDrawable) { final AsyncAvatarDrawable asyncDrawable = (AsyncAvatarDrawable) drawable; return asyncDrawable.getAvatarWorkerTask(); @@ -602,18 +697,18 @@ public ThumbnailGenerationTask getBitmapWorkerTask() { } public static class AsyncAvatarDrawable extends BitmapDrawable { - private final WeakReference avatarWorkerTaskReference; + private final WeakReference avatarWorkerTaskReference; public AsyncAvatarDrawable( - Resources res, Bitmap bitmap, AvatarGenerationTask avatarWorkerTask + Resources res, Bitmap bitmap, GetAvatarTask avatarWorkerTask ) { super(res, bitmap); avatarWorkerTaskReference = - new WeakReference(avatarWorkerTask); + new WeakReference(avatarWorkerTask); } - public AvatarGenerationTask getAvatarWorkerTask() { + public GetAvatarTask getAvatarWorkerTask() { return avatarWorkerTaskReference.get(); } } diff --git a/src/com/owncloud/android/datamodel/UserProfile.java b/src/com/owncloud/android/datamodel/UserProfile.java new file mode 100644 index 00000000000..2f0324c4364 --- /dev/null +++ b/src/com/owncloud/android/datamodel/UserProfile.java @@ -0,0 +1,93 @@ +package com.owncloud.android.datamodel; + +import android.support.annotation.Nullable; + +/** + * ownCloud Android client application + * + * @author David A. Velasco + * Copyright (C) 2016 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +public class UserProfile { + + private long mId; + private String mAccountName; + + private String mUserId; + private String mDisplayName = ""; + private String mEmail = ""; + + private UserAvatar mAvatar; + + public UserProfile(String accountName, String userId, String displayName, String email) { + mAccountName = accountName; + mUserId = userId; + mDisplayName = displayName; + mEmail = email; + + mAvatar = null; + } + + public String getAccountName() { + return mAccountName; + } + + public String getUserId() { + return mUserId; + } + + public String getDisplayName() { + return mDisplayName; + } + + public String getEmail() { + return mEmail; + } + + @Nullable + public UserAvatar getAvatar() { + return mAvatar; + } + + public void setAvatar(UserAvatar avatar) { + mAvatar = avatar; + } + + public static class UserAvatar { + + private String mCacheKey; + private String mMimeType; + private String mEtag; + + public UserAvatar(String cacheKey, String mimeType, String etag) { + mCacheKey = cacheKey; + mMimeType = mimeType; + mEtag = etag; + } + + public String getCacheKey() { + return mCacheKey; + } + + public String getMimeType() { + return mMimeType; + } + + public String getEtag() { + return mEtag; + } + } +} diff --git a/src/com/owncloud/android/datamodel/UserProfilesRepository.java b/src/com/owncloud/android/datamodel/UserProfilesRepository.java new file mode 100644 index 00000000000..4dbbd6899b3 --- /dev/null +++ b/src/com/owncloud/android/datamodel/UserProfilesRepository.java @@ -0,0 +1,194 @@ +package com.owncloud.android.datamodel; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import com.owncloud.android.MainApp; +import com.owncloud.android.db.ProviderMeta; +import com.owncloud.android.lib.common.utils.Log_OC; + +import java.io.File; + +/** + * ownCloud Android client application + * + * Copyright (C) 2016 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * Minimum to get things working. + * + * Working around FileContentProvider, we have no interest in exporting user profiles to other apps. + */ +public class UserProfilesRepository { + + private static final String TAG = UserProfilesRepository.class.getName(); + + private SQLiteDatabase mDb; + + public UserProfilesRepository() { + File dbFile = MainApp.getAppContext().getDatabasePath(ProviderMeta.DB_NAME); + mDb = SQLiteDatabase.openDatabase( + dbFile.getAbsolutePath(), + null, + SQLiteDatabase.OPEN_READWRITE + ); + } + + /** + * Persist a user profile. + * + * Minimum to get things working: only storing info about avatar. + * + * Working around ContentProvider + * + * @param userProfile User profile. + */ + public void update(UserProfile userProfile) { + + if (userProfile == null) { + throw new IllegalArgumentException("Received userProfile with NULL value"); + } + + if (userProfile.getAvatar() != null) { + // map avatar properties to columns + ContentValues avatarValues = new ContentValues(); + avatarValues.put( + ProviderMeta.ProviderTableMeta.USER_AVATARS__ACCOUNT_NAME, + userProfile.getAccountName() + ); + avatarValues.put( + ProviderMeta.ProviderTableMeta.USER_AVATARS__CACHE_KEY, + userProfile.getAvatar().getCacheKey() + ); + avatarValues.put( + ProviderMeta.ProviderTableMeta.USER_AVATARS__ETAG, + userProfile.getAvatar().getEtag() + ); + avatarValues.put( + ProviderMeta.ProviderTableMeta.USER_AVATARS__MIME_TYPE, + userProfile.getAvatar().getMimeType() + ); + + mDb.beginTransaction(); + try { + if (avatarExists(userProfile)) { + // not new, UPDATE + int count = mDb.update( + ProviderMeta.ProviderTableMeta.USER_AVATARS__TABLE_NAME, + avatarValues, + ProviderMeta.ProviderTableMeta.USER_AVATARS__ACCOUNT_NAME + "=?", + new String[]{String.valueOf(userProfile.getAccountName())} + ); + Log_OC.d(TAG, "Avatar updated"); + + } else { + // new, CREATE + mDb.insert( + ProviderMeta.ProviderTableMeta.USER_AVATARS__TABLE_NAME, + null, + avatarValues + ); + Log_OC.d(TAG, "Avatar inserted"); + } + mDb.setTransactionSuccessful(); + + } finally { + mDb.endTransaction(); + } + } + } + + /** + * Gets the information about a user avatar bound to an OC account. + * + * Shortcut method prevent retrieving a full {@link UserProfile}, + * specially now that {@link UserProfile}s are not really stored. Naughty trick. + * + * @param accountName Name of an OC account. + * @return Information about a user avatar bound to an OC account, or NULL if + * there is no avatar for the given account. + */ + public UserProfile.UserAvatar getAvatar(String accountName) { + UserProfile.UserAvatar avatar = null; + Cursor c = null; + try { + c = mDb.query( + ProviderMeta.ProviderTableMeta.USER_AVATARS__TABLE_NAME, + null, + ProviderMeta.ProviderTableMeta.USER_AVATARS__ACCOUNT_NAME + "=?", + new String[]{accountName}, + null, null, null + ); + if (c != null && c.moveToFirst()) { + avatar = new UserProfile.UserAvatar( + c.getString(c.getColumnIndex( + ProviderMeta.ProviderTableMeta.USER_AVATARS__CACHE_KEY + )), + c.getString(c.getColumnIndex( + ProviderMeta.ProviderTableMeta.USER_AVATARS__MIME_TYPE + )), + c.getString( + c.getColumnIndex(ProviderMeta.ProviderTableMeta.USER_AVATARS__ETAG + )) + ); + } // else, no avatar to return + } catch (Exception e) { + Log_OC.e(TAG, "Exception while querying avatar", e); + } finally { + if (c != null) { + c.close(); + } + } + return avatar; + } + + public void deleteAvatar(String accountName) { + try { + mDb.delete( + ProviderMeta.ProviderTableMeta.USER_AVATARS__TABLE_NAME, + ProviderMeta.ProviderTableMeta.USER_AVATARS__ACCOUNT_NAME + "=?", + new String[]{String.valueOf(accountName)} + ); + Log_OC.d(TAG, "Avatar deleted"); + + } catch (Exception e) { + Log_OC.e(TAG, "Exception while deleting avatar", e); + } + } + + private boolean avatarExists(UserProfile userProfile) { + boolean exists = false; + Cursor c = null; + try { + c = mDb.query( + ProviderMeta.ProviderTableMeta.USER_AVATARS__TABLE_NAME, + null, + ProviderMeta.ProviderTableMeta.USER_AVATARS__ACCOUNT_NAME + "=?", + new String[]{userProfile.getAccountName()}, + null, null, null + ); + exists = (c != null && c.moveToFirst()); + } finally { + if (c != null) { + c.close(); + } + } + return exists; + } + +} diff --git a/src/com/owncloud/android/db/ProviderMeta.java b/src/com/owncloud/android/db/ProviderMeta.java index e7cc5329a34..cfce7ce9456 100644 --- a/src/com/owncloud/android/db/ProviderMeta.java +++ b/src/com/owncloud/android/db/ProviderMeta.java @@ -33,7 +33,7 @@ public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 14; + public static final int DB_VERSION = 15; private ProviderMeta() { } @@ -43,6 +43,8 @@ static public class ProviderTableMeta implements BaseColumns { public static final String OCSHARES_TABLE_NAME = "ocshares"; public static final String CAPABILITIES_TABLE_NAME = "capabilities"; public static final String UPLOADS_TABLE_NAME = "list_of_uploads"; + public static final String USER_AVATARS__TABLE_NAME = "user_avatars"; + public static final Uri CONTENT_URI = Uri.parse("content://" + MainApp.getAuthority() + "/"); public static final Uri CONTENT_URI_FILE = Uri.parse("content://" @@ -151,5 +153,11 @@ static public class ProviderTableMeta implements BaseColumns { public static final String UPLOADS_DEFAULT_SORT_ORDER = ProviderTableMeta._ID + " collate nocase desc"; + + // Columns of user_avatars table + public static final String USER_AVATARS__ACCOUNT_NAME = "account_name"; + public static final String USER_AVATARS__CACHE_KEY = "cache_key"; + public static final String USER_AVATARS__ETAG = "etag"; + public static final String USER_AVATARS__MIME_TYPE = "mime_type"; } } diff --git a/src/com/owncloud/android/operations/GetUserProfileOperation.java b/src/com/owncloud/android/operations/GetUserProfileOperation.java index 0ece675254e..e5985093b71 100644 --- a/src/com/owncloud/android/operations/GetUserProfileOperation.java +++ b/src/com/owncloud/android/operations/GetUserProfileOperation.java @@ -21,15 +21,24 @@ import android.accounts.Account; import android.accounts.AccountManager; +import android.content.res.Resources; import com.owncloud.android.MainApp; +import com.owncloud.android.R; +import com.owncloud.android.datamodel.ThumbnailsCacheManager; +import com.owncloud.android.datamodel.UserProfile; +import com.owncloud.android.datamodel.UserProfilesRepository; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.lib.resources.users.GetRemoteUserAvatarOperation; import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation; import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation.UserInfo; import com.owncloud.android.operations.common.SyncOperation; +import java.util.ArrayList; + /** * Get and save user's profile from the server. * @@ -37,6 +46,9 @@ */ public class GetUserProfileOperation extends SyncOperation { + private static final String TAG = GetUserProfileOperation.class.getName(); + + /** * Performs the operation. * @@ -51,22 +63,110 @@ public class GetUserProfileOperation extends SyncOperation { @Override protected RemoteOperationResult run(OwnCloudClient client) { - // get display name - GetRemoteUserInfoOperation getDisplayName = new GetRemoteUserInfoOperation(); - RemoteOperationResult result = getDisplayName.execute(client); - - if (result.isSuccess()) { - // store display name with account data - AccountManager accountManager = AccountManager.get(MainApp.getAppContext()); - UserInfo userInfo = (UserInfo) result.getData().get(0); - Account storedAccount = getStorageManager().getAccount(); - accountManager.setUserData( - storedAccount, - AccountUtils.Constants.KEY_DISPLAY_NAME, - userInfo.mDisplayName - ); + UserProfile userProfile = null; + RemoteOperationResult result = null; + + try { + /// get display name + GetRemoteUserInfoOperation getDisplayName = new GetRemoteUserInfoOperation(); + RemoteOperationResult remoteResult = getDisplayName.execute(client); + if (remoteResult.isSuccess()) { + // store display name with account data + AccountManager accountManager = AccountManager.get(MainApp.getAppContext()); + UserInfo userInfo = (UserInfo) remoteResult.getData().get(0); + Account storedAccount = getStorageManager().getAccount(); + accountManager.setUserData( + storedAccount, + AccountUtils.Constants.KEY_DISPLAY_NAME, // keep also there, for the moment + userInfo.mDisplayName + ); + + // map user info into UserProfile instance + userProfile = new UserProfile( + storedAccount.name, + userInfo.mId, + userInfo.mDisplayName, + userInfo.mEmail + ); + + /// get avatar (optional for success) + int dimension = getAvatarDimension(); + UserProfile.UserAvatar currentUserAvatar = + getUserProfilesRepository().getAvatar(storedAccount.name); + GetRemoteUserAvatarOperation getAvatarOperation = new GetRemoteUserAvatarOperation( + dimension, + (currentUserAvatar == null) ? "" : currentUserAvatar.getEtag() + ); + remoteResult = getAvatarOperation.execute(client); + if (remoteResult.isSuccess()) { + GetRemoteUserAvatarOperation.ResultData avatar = + (GetRemoteUserAvatarOperation.ResultData) remoteResult.getData().get(0); + + // + byte[] avatarData = avatar.getAvatarData(); + String avatarKey = ThumbnailsCacheManager.addAvatarToCache( + storedAccount.name, + avatarData, + dimension + ); + + UserProfile.UserAvatar userAvatar = new UserProfile.UserAvatar( + avatarKey, avatar.getMimeType(), avatar.getEtag() + ); + userProfile.setAvatar(userAvatar); + + } else if (remoteResult.getCode().equals( + RemoteOperationResult.ResultCode.FILE_NOT_FOUND + )) { + Log_OC.i(TAG, "No avatar available, removing cached copy"); + getUserProfilesRepository().deleteAvatar(storedAccount.name); + ThumbnailsCacheManager.removeAvatarFromCache(storedAccount.name); + + } // others are ignored, including 304 (not modified), so the avatar is only stored + // if changed in the server :D + + /// store userProfile + getUserProfilesRepository().update(userProfile); + + result = new RemoteOperationResult(RemoteOperationResult.ResultCode.OK); + ArrayList data = new ArrayList<>(); + data.add(userProfile); + result.setData(data); + + } else { + result = remoteResult; + } + } catch (Exception e) { + Log_OC.e(TAG, "Exception while getting user profile: ", e); + result = new RemoteOperationResult(e); } + return result; } + /** + * Converts size of file icon from dp to pixel + * @return int + */ + private int getAvatarDimension(){ + // Converts dp to pixel + Resources r = MainApp.getAppContext().getResources(); + return Math.round(r.getDimension(R.dimen.file_avatar_size)); + } + + + /** + * Really bad place to have this. Only here to prevent go further with refactoring. + * + * @return Reference to a {@link UserProfilesRepository}. + */ + private static UserProfilesRepository getUserProfilesRepository() { + if (sUserProfilesRepository == null) { + sUserProfilesRepository = new UserProfilesRepository(); + } + return sUserProfilesRepository; + } + + private static UserProfilesRepository sUserProfilesRepository; + } diff --git a/src/com/owncloud/android/operations/RefreshFolderOperation.java b/src/com/owncloud/android/operations/RefreshFolderOperation.java index 0669b8eb874..bd76b843f09 100644 --- a/src/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/src/com/owncloud/android/operations/RefreshFolderOperation.java @@ -144,10 +144,10 @@ public RefreshFolderOperation(OCFile folder, mStorageManager = dataStorageManager; mAccount = account; mContext = context; - mForgottenLocalFiles = new HashMap(); + mForgottenLocalFiles = new HashMap<>(); mRemoteFolderChanged = false; mIgnoreETag = ignoreETag; - mFilesToSyncContents = new Vector(); + mFilesToSyncContents = new Vector<>(); } @@ -180,7 +180,7 @@ public List getChildren() { */ @Override protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result = null; + RemoteOperationResult result; mFailsInFavouritesFound = 0; mConflictsFound = 0; mForgottenLocalFiles.clear(); @@ -247,7 +247,7 @@ private void updateUserProfile() { if (!result.isSuccess()) { Log_OC.w(TAG, "Couldn't update user profile from server"); } else { - Log_OC.i(TAG, "Got display name: " + result.getData().get(0)); + Log_OC.i(TAG, "Got user profile"); } } @@ -261,7 +261,7 @@ private void updateCapabilities(){ private RemoteOperationResult checkForChanges(OwnCloudClient client) { mRemoteFolderChanged = true; - RemoteOperationResult result = null; + RemoteOperationResult result; String remotePath = mLocalFolder.getRemotePath(); Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath); @@ -459,7 +459,7 @@ private void synchronizeData(ArrayList folderAndFiles, OwnCloudClient cl private void startContentSynchronizations( List filesToSyncContents, OwnCloudClient client ) { - RemoteOperationResult contentsResult = null; + RemoteOperationResult contentsResult; for (SynchronizeFileOperation op: filesToSyncContents) { contentsResult = op.execute(mStorageManager, mContext); // async if (!contentsResult.isSuccess()) { @@ -488,7 +488,7 @@ private void startContentSynchronizations( * the operation. */ private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) { - RemoteOperationResult result = null; + RemoteOperationResult result; // remote request GetRemoteSharesForFileOperation operation = @@ -497,7 +497,7 @@ private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) { if (result.isSuccess()) { // update local database - ArrayList shares = new ArrayList(); + ArrayList shares = new ArrayList<>(); for(Object obj: result.getData()) { shares.add((OCShare) obj); } @@ -515,7 +515,6 @@ private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) { * @param event * @param dirRemotePath Remote path of a folder that was just synchronized * (with or without success) - * @param result */ private void sendLocalBroadcast( String event, String dirRemotePath, RemoteOperationResult result diff --git a/src/com/owncloud/android/providers/FileContentProvider.java b/src/com/owncloud/android/providers/FileContentProvider.java index ef2ce0063d5..0320c1a4464 100644 --- a/src/com/owncloud/android/providers/FileContentProvider.java +++ b/src/com/owncloud/android/providers/FileContentProvider.java @@ -534,6 +534,8 @@ public void onCreate(SQLiteDatabase db) { // Create uploads table createUploadsTable(db); + // Create user profiles table + createUserProfilesTable(db); } @Override @@ -763,6 +765,19 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } + if (oldVersion < 15 && newVersion >= 15) { + Log_OC.i("SQL", "Entering in the #15 ADD in onUpgrade"); + db.beginTransaction(); + try { + // Create user profiles table + createUserProfilesTable(db); + upgraded = true; + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + if (!upgraded) Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion); @@ -863,18 +878,20 @@ private void createUploadsTable(SQLiteDatabase db){ + ProviderTableMeta.UPLOADS_LAST_RESULT + " INTEGER, " // Upload LastResult + ProviderTableMeta.UPLOADS_CREATED_BY + " INTEGER );" // Upload createdBy ); + } - - /* before: - // PRIMARY KEY should always imply NOT NULL. Unfortunately, due to a - // bug in some early versions, this is not the case in SQLite. - //db.execSQL("CREATE TABLE " + TABLE_UPLOAD + " (" + " path TEXT PRIMARY KEY NOT NULL UNIQUE," - // + " uploadStatus INTEGER NOT NULL, uploadObject TEXT NOT NULL);"); - // uploadStatus is used to easy filtering, it has precedence over - // uploadObject.getUploadStatus() - */ + private void createUserProfilesTable(SQLiteDatabase db) { + db.execSQL("CREATE TABLE " + ProviderTableMeta.USER_AVATARS__TABLE_NAME + "(" + + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, " + + ProviderTableMeta.USER_AVATARS__ACCOUNT_NAME + " TEXT, " + + ProviderTableMeta.USER_AVATARS__CACHE_KEY + " TEXT, " + + ProviderTableMeta.USER_AVATARS__MIME_TYPE + " TEXT, " + + ProviderTableMeta.USER_AVATARS__ETAG + " TEXT );" + ); } + + /** * Version 10 of database does not modify its scheme. It coincides with the upgrade of the ownCloud account names * structure to include in it the path to the server instance. Updating the account names and path to local files diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java index 70bd167dec8..d1481a64090 100644 --- a/src/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java @@ -24,7 +24,6 @@ import android.accounts.AccountManagerFuture; import android.content.Intent; import android.content.res.Configuration; -import android.graphics.Bitmap; import android.os.Build; import android.os.Bundle; import android.support.design.widget.NavigationView; @@ -44,8 +43,6 @@ import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.ui.DefaultAvatarTextDrawable; -import com.owncloud.android.utils.BitmapUtils; import com.owncloud.android.utils.DisplayUtils; /** @@ -119,7 +116,7 @@ public abstract class DrawerActivity extends ToolbarActivity { /** * accounts for the (max) three displayed accounts in the drawer header. */ - private Account[] mAvatars = new Account[3]; + private Account[] mAccountsWithAvatars = new Account[3]; /** * Initializes the drawer, its content and highlights the menu item with the given id. @@ -342,20 +339,26 @@ public void updateAccountList() { populateDrawerOwnCloudAccounts(); // activate second/end account avatar - if (mAvatars[1] != null) { - DisplayUtils.setAvatar(mAvatars[1], - (ImageView) findNavigationViewChildById(R.id.drawer_account_end), - mOtherAccountAvatarRadiusDimension, getResources()); + if (mAccountsWithAvatars[1] != null) { + DisplayUtils.showAccountAvatar( + mAccountsWithAvatars[1], + (ImageView) findNavigationViewChildById(R.id.drawer_account_end), + mOtherAccountAvatarRadiusDimension, + false + ); mAccountEndAccountAvatar.setVisibility(View.VISIBLE); } else { mAccountEndAccountAvatar.setVisibility(View.GONE); } // activate third/middle account avatar - if (mAvatars[2] != null) { - DisplayUtils.setAvatar(mAvatars[2], - (ImageView) findNavigationViewChildById(R.id.drawer_account_middle), - mOtherAccountAvatarRadiusDimension, getResources()); + if (mAccountsWithAvatars[2] != null) { + DisplayUtils.showAccountAvatar( + mAccountsWithAvatars[2], + (ImageView) findNavigationViewChildById(R.id.drawer_account_middle), + mOtherAccountAvatarRadiusDimension, + false + ); mAccountMiddleAccountAvatar.setVisibility(View.VISIBLE); } else { mAccountMiddleAccountAvatar.setVisibility(View.GONE); @@ -386,27 +389,14 @@ private void repopulateAccountList(Account[] accounts) { MENU_ORDER_ACCOUNT, accounts[i].name ); - try { - setAvatar(accounts[i], accountMenuItem); - - } catch (Exception e1) { - Log_OC.w(TAG, "Error retrieving avatar, generating colored letter instead"); - try { - accountMenuItem.setIcon( - DefaultAvatarTextDrawable.createAvatar( - accounts[i].name, - mMenuAccountAvatarRadiusDimension - ) - ); - - } catch (Exception e2) { - Log_OC.w( - TAG, - "Error calculating color for account icon, using static icon instead" - ); - accountMenuItem.setIcon(R.drawable.ic_user); - } - } + ThumbnailsCacheManager.GetAvatarTask task = + new ThumbnailsCacheManager.GetAvatarTask( + accountMenuItem, + accounts[i], + mMenuAccountAvatarRadiusDimension, + false + ); + task.execute(); } } @@ -462,61 +452,12 @@ protected void setAccountInDrawer(Account account) { username.setText(AccountUtils.getUsernameOfAccount(account.name)); } - DisplayUtils.setAvatar(account, (ImageView) findNavigationViewChildById(R.id.drawer_current_account), - mCurrentAccountAvatarRadiusDimension, getResources()); - } - } - - /** - * fetches and sets the avatar of the current account in the drawer in case the drawer is available. - * - * @param account the account to be set in the drawer - * @param menuItem the menuItem to set the avatar on - */ - private void setAvatar(Account account, MenuItem menuItem) { - if (mDrawerLayout != null && account != null) { - - // Thumbnail in Cache? - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + account.name); - - if (thumbnail != null) { - menuItem.setIcon( - BitmapUtils.bitmapToCircularBitmapDrawable(MainApp.getAppContext().getResources(), thumbnail) - ); - } else { - // generate new avatar - if (ThumbnailsCacheManager.cancelPotentialAvatarWork(account.name, menuItem)) { - final ThumbnailsCacheManager.AvatarGenerationTask task = - new ThumbnailsCacheManager.AvatarGenerationTask( - menuItem, account - ); - if (thumbnail == null) { - try { - menuItem.setIcon( - DefaultAvatarTextDrawable.createAvatar( - account.name, - mMenuAccountAvatarRadiusDimension - ) - ); - } catch (Exception e) { - Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); - menuItem.setIcon(R.drawable.ic_account_circle); - } - } else { - final ThumbnailsCacheManager.AsyncAvatarDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncAvatarDrawable( - getResources(), - thumbnail, - task - ); - menuItem.setIcon( - BitmapUtils.bitmapToCircularBitmapDrawable( - MainApp.getAppContext().getResources(), asyncDrawable.getBitmap()) - ); - } - task.execute(account.name); - } - } + DisplayUtils.showAccountAvatar( + account, + (ImageView) findNavigationViewChildById(R.id.drawer_current_account), + mCurrentAccountAvatarRadiusDimension, + false + ); } } @@ -685,16 +626,16 @@ protected void onAccountCreationSuccessful(AccountManagerFuture future) * always the current account. */ private void populateDrawerOwnCloudAccounts() { - mAvatars = new Account[3]; + mAccountsWithAvatars = new Account[3]; Account[] accountsAll = AccountManager.get(this).getAccountsByType (MainApp.getAccountType()); Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(this); - mAvatars[0] = currentAccount; + mAccountsWithAvatars[0] = currentAccount; int j = 0; for (int i = 1; i <= 2 && i < accountsAll.length && j < accountsAll.length; j++) { if (!currentAccount.equals(accountsAll[j])) { - mAvatars[i] = accountsAll[j]; + mAccountsWithAvatars[i] = accountsAll[j]; i++; } } diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index c8029dc7916..78b03448a83 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -239,8 +239,7 @@ public void onRequestPermissionsResult(int requestCode, if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted - startSynchronization(); - // toggle on is save since this is the only scenario this code gets accessed + startSyncFolderOperation(getFile(), false); } else { // permission denied --> do nothing } diff --git a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java index 6ea2114302b..db0cbf96a17 100644 --- a/src/com/owncloud/android/ui/adapter/AccountListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/AccountListAdapter.java @@ -105,8 +105,12 @@ public View getView(final int position, View convertView, ViewGroup parent) { ); try { - DisplayUtils.setAvatar(account, viewHolder.imageViewItem, mAccountAvatarRadiusDimension, - mContext.getResources()); + DisplayUtils.showAccountAvatar( + account, + viewHolder.imageViewItem, + mAccountAvatarRadiusDimension, + true + ); } catch (Exception e) { Log_OC.e(TAG, "Error calculating RGB value for account list item.", e); // use user icon as a fallback diff --git a/src/com/owncloud/android/utils/DisplayUtils.java b/src/com/owncloud/android/utils/DisplayUtils.java index 8a01068f94f..ef96c43a42f 100644 --- a/src/com/owncloud/android/utils/DisplayUtils.java +++ b/src/com/owncloud/android/utils/DisplayUtils.java @@ -301,52 +301,38 @@ public static void colorSnackbar(Context context, Snackbar snackbar) { } /** - * fetches and sets the avatar of the current account in the drawer in case the drawer is available. + * Show the avatar corresponding to the received account in an {@ImageView}. + * + * The avatar is shown if available locally in {@link ThumbnailsCacheManager}. The avatar is not + * fetched from the server if not available. + * + * If there is no avatar stored, a colored icon is generated with the first letter of the account username. + * + * If this is not possible either, a predefined user icon is shown instead. + * + * @param account OC account which avatar will be shown. + * @param displayView The image view to set the avatar on. + * @param displayRadius The radius of the circle where the avatar will be clipped into. + * @param fetchFromServer When 'true', if there is no avatar stored in the cache, it's fetched from + * the server. When 'false', server is not accessed, the fallback avatar is + * generated instead. USE WITH CARE, probably to be removed in the future. * - * @param account the account to be set in the drawer - * @param userIcon the image view to set the avatar on - * @param avatarRadius the avatar radius - * @param resources reference for density information */ - public static void setAvatar(Account account, ImageView userIcon, float avatarRadius, Resources resources) { + public static void showAccountAvatar( + Account account, + ImageView displayView, + float displayRadius, + boolean fetchFromServer + ) { if (account != null) { + // not just accessibility support, used to know what account is bound to each imageView + displayView.setContentDescription(account.name); - userIcon.setContentDescription(account.name); - - // Thumbnail in Cache? - Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + account.name); - - if (thumbnail != null) { - userIcon.setImageDrawable( - BitmapUtils.bitmapToCircularBitmapDrawable( - MainApp.getAppContext().getResources(), thumbnail - ) + final ThumbnailsCacheManager.GetAvatarTask task = + new ThumbnailsCacheManager.GetAvatarTask( + displayView, account, displayRadius, fetchFromServer ); - } else { - // generate new avatar - if (ThumbnailsCacheManager.cancelPotentialAvatarWork(account.name, userIcon)) { - final ThumbnailsCacheManager.AvatarGenerationTask task = - new ThumbnailsCacheManager.AvatarGenerationTask(userIcon, account); - if (thumbnail == null) { - try { - userIcon.setImageDrawable( - DefaultAvatarTextDrawable.createAvatar(account.name, avatarRadius) - ); - } catch (Exception e) { - Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); - userIcon.setImageResource(R.drawable.ic_account_circle); - } - } else { - final ThumbnailsCacheManager.AsyncAvatarDrawable asyncDrawable = - new ThumbnailsCacheManager.AsyncAvatarDrawable(resources, thumbnail, task); - userIcon.setImageDrawable( - BitmapUtils.bitmapToCircularBitmapDrawable( - MainApp.getAppContext().getResources(), asyncDrawable.getBitmap()) - ); - } - task.execute(account.name); - } - } + task.execute(); } } }