Showing only Logged-in Accounts in the Sharing Page of Phimpme Android

In Phimpme Android application, users can edit their pictures and share them to a number of platforms ranging from social networking sites like Facebook, Twitter etc to cloud storage and image hosting sites like Box, Dropbox, Imgur etc.

Desired flow of the application

According to the flow of the application, the user has to add an account first i.e. log in to the particular account that needs to be connected to the application. After that when the user enters the share page for sharing the image, a button corresponding to the connected account is visible in that page which on clicking will share the image to that platform directly.

What was happening previously?

The list of accounts which is present in the account manager of Phimpme Android application is also getting displayed in the share image page. As the list is large, it is difficult for the user to find the connected account from the list. There is not even an indicator whether a particular account is connected or not. On clicking the button corresponding to the non-connected account, an error dialog instructing the user to log in from the account manager first, will get displayed.

How we solved it?

We first thought of just adding an indicator on the buttons in the accounts page to show whether it is connected or not. But this fix solves only a single issue. Find the connected account in that large list will be difficult for the user even then. So we decided to remove the whole list and show only the accounts which are connected previously in account manager. This cleans the flow of the accounts and share in  Phimpme Android application

When a user logins from the account manager, the credentials, tokens and other details corresponding to that accounts gets saved in database. We used realm database for saving the details in our application. As the details are present in this database, the list can be dynamically generated when the user opens share image page. We implemented a function in Utils class for getting the list of logged in accounts. Its implementation is shown below.

public static boolean checkAlreadyExist(AccountDatabase.AccountName s) {

   Realm realm = Realm.getDefaultInstance();

   RealmQuery<AccountDatabase> query = realm.where(AccountDatabase.class);

   query.equalTo("name", s.toString());

   RealmResults<AccountDatabase> result1 = query.findAll();

   return (result1.size() > 0);

}



public static ArrayList<AccountDatabase.AccountName> getLoggedInAccountsList(){

   ArrayList<AccountDatabase.AccountName> list = new ArrayList<>();

   for (AccountDatabase.AccountName account : AccountDatabase.AccountName.values()){

       if (checkAlreadyExist(account))

           list.add(account);

   }

   return list;

}

Additional changes

There are few accounts which don’t need authentication. Those accounts need their respective application to be installed in the user’s device. So for adding those accounts to the list, we added another function which checks whether a particular package is installed in user’s device or not. Using that it adds the account to the list. The implementation for checking whether a package is installed or not is shown below.

public static boolean isAppInstalled(String packageName, PackageManager pm) {

   boolean installed;

   try {

       pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);

       installed = true;

   } catch (PackageManager.NameNotFoundException e) {

       installed = false;

   }

   return installed;

}

                 

Resources:

Shortcuts in the Phimpme Android Application

The Phimpme Android application comes with a great functionality of capturing moments, editing them, and sharing it with the world using various social media or cloud platforms integrated into the application. Sometimes, the user may want to directly go to a particular section of the application without having to go to the home activity all the time. We have solved this issue using the App shortcuts option which is provided on Android versions greater than 7.0 Nougat. When the user long clicks on the icon of the application, it provides us with multiple options to choose which activity or section we would like to go to as depicted in the screenshot below.

In this post, I will be explaining how we have achieved this functionality in the Phimpme Android application.

Step 1

In the Phimpme app, we have three main sections named Camera, Gallery and Accounts and we have added these three options in the app shortcut menu. To do this, first of all, we need to add a meta data to the main activity in the AndroidManifest.xml file. To do this, search for the activity which contains “android.intent.action.MAIN” and add the following line of code in the activity tag.

<meta-data android:name="android.app.shortcuts"
  android:resource="@xml/shortcuts" />

Step 2

Now after we have configured the manifest file, we need to create shortcuts.xml file in the resources folder which will contain the information about our app shortcuts along with the intent action to go to a particular activity.

After creating the xml file, add the following line of code.

<shortcuts xmlns:tools="http://schemas.android.com/tools"
  xmlns:android="http://schemas.android.com/apk/res/android">

Now after this we have to create a shortcut tag and define the id of our shortcut and should add the icon to it which will be displayed to the user along with the shortcut text as depicted in the screenshot above. This can be done using the following line of code.

android:shortcutId="camera"
android:enabled="true"
android:icon="@drawable/ic_camera_alt_black_24dp"
android:shortcutShortLabel="@string/camera_short"
android:shortcutLongLabel="@string/camera_long"
android:shortcutDisabledMessage="@string/camera_short"

Now after this, we have to define the action which we want to perform when the user clicks on a particular app shortcut. To do this, create an intent tag and add the following lines of code in it.

<intent
  android:action="android.media.action.IMAGE_CAPTURE_SECURE"
  android:targetPackage="org.fossasia.phimpme"
  android:targetClass="org.fossasia.phimpme.opencamera.Camera.CameraActivity" />
<categories android:name="android.intent.category.DEFAULT" />

The above code sets the action type as Image capture and defines the package name of the application. The above code is used to open the Camera activity of the Phimpme Application from the shortcut menu. In the similar fashion as described above, we have implemented the option to open up the Gallery and Accounts section of our application by just modifying the action name of the shortcut.

This is how we have implemented the App shortcut functionality in the Phimpme app. To get the full source code of the Shortcuts.xml file, please refer to the Phimpme Android repository.

Resources

  1. Android Developer’s Guide – App shortcuts – https://developer.android.com/guide/topics/ui/shortcuts.html
  2. GitHub – Google Sample Project to depict the usage of app shortcuts – https://github.com/googlesamples/android-AppShortcuts
  3. Blog – Using app shortcuts in Android 7.0 – http://www.brevitysoftware.com/blog/how-to-use-app-shortcuts-in-android-7-1-nougat/
  4. GitHub – Phimpme Android repository – https://github.com/fossasia/phimpme-android/

Reset SUSI.AI User Password & Parameter extraction from Link

In this blog I will discuss how does Accounts handle the incoming request to reset the password. If a user forgets his/her password, They can use forgot password button on http://accounts.susi.ai and It’s implementation is quite straightforward. As soon as a user enter his/her e-mail id and hits RESET button, an ajax call is made which first checks if the user email id is registered with SUSI.AI or not. If not, a failure message is thrown to user. But if the user is found to be registered in the database, An email is sent to him/her which contains a 30 characters long token. On the server token is hashed against the user’s email id and a validity of 7 days is set to it.
Let us have a look at the Reset Password link one receives.

http://accounts.susi.ai/?token={30 characters long token}

On clicking this link, what it does is that user is redirected to http://accounts.susi.ai with token as a request parameter. At the client side, A search is made which evaluates whether the URL has a token parameter or not. This was the overview. Since, http://accounts.susi.ai is based on ReactJS framework, it is not easy alike the native php functionality, but much more logical and systematic. Let us now take a closer look at how this parameter is searched for, token extracted and validated.

As you can see http://accounts.susi.ai and http://accounts.susi.ai/?token={token}, Both redirect the user to the same URL. So the first task that needs to be accomplished is check if a parameter is passed in the URL or not.
First import addUrlProps and UrlQueryParamTypes from react-url-query package and PropTypes from prop-types package. These will be required in further steps.
Have a look at the code and then we will understand it’s working.

const urlPropsQueryConfig = {
  token: { type: UrlQueryParamTypes.string },
};

class Login extends Component {
	static propTypes = {
    // URL props are automatically decoded and passed in based on the config
    token: PropTypes.string,
    // change handlers are automatically generated when given a config.
    // By default they update that single query parameter and maintain existing
    // values in the other parameters.
    onChangeToken: PropTypes.func,
  }

  static defaultProps = {
    token: "null",
  }

Above in the first step, we have defined by what parameter should the Reset Password triggered. It means, if and only if the incoming parameter in the URL is token, we move to next step, otherwise normal http://accounts.susi.ai page should be rendered. Also we have defined the data type of the token parameter to be UrlQueryParamTypes.string.
PropTypes are attributes in ReactJS similar to tags in HTML. So again, we have defined the data type. onChangeToken is a custom attribute which is fired whenever token parameter is modified. To declare default values, we have used defaultProps function. If token is not passed in the URL, by default it makes it null. This is still not the last step. Till now we have not yet checked if token is there or not. This is done in the componentDidMount function. componentDidMount is a function which gets called before the client renders the page. In this function, we will check that if the value of token is not equal to null, then a value must have been passed to it. If the value of token is not equal to null, it extracts the token from the URL and redirects user to reset password application. Below is the code snippet for better understanding.

componentDidMount() {
		const {
      token
    } = this.props;
		console.log(token)
		if(token !== "null") {
			this.props.history.push('/resetpass/?token='+token);
		}
		if(cookies.get('loggedIn')) {
			this.props.history.push('/userhome', { showLogin: false });
		}
	}

With this the first step of the process has been achieved. Again in the second step, we extract the token from the URL in the similar above fashion followed by an ajax request to the server which will validate the token and sends response to client accordingly {token might be invalid or expired}. Success is encoded in a JSON object and sent to client. Error is thrown as an error only and client shows an error of “invalid token”. If it is a success, it sends email id of the user against which the token was hashed and the client displays it on the page. Now user has to enter new password and confirm it. Client also tests whether both the fields have same values or not. If they are same, a simple ajax call is made to server with new password and the email id and token. If no error is caused in between (like connection timeout, server might be down etc), client is sent a JSON response again with “accepted” flag = true and message as “Your password has been changed!”.

Additional Resources:

(npmjs – official documentation of create-react-apps)

(Fortnox developer’s blog post)

(Dave Ceddia’s blog post for new ReactJS developers)