diff --git a/app/build.gradle b/app/build.gradle index 68cb737..49cf2c0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,7 +20,7 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) + compile fileTree(include: ['*.jar'], dir: 'libs') testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.1.1' compile 'io.reactivex:rxandroid:1.0.1' @@ -32,4 +32,6 @@ dependencies { compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.0' compile 'com.jakewharton:butterknife:7.0.1' compile 'com.jakewharton.timber:timber:2.4.2' + compile 'com.android.support:recyclerview-v7:23.1.1' + compile 'com.android.support:design:23.1.1' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2856210..14e391d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,23 +1,31 @@ + package="io.leftshift.sample"> - + + + android:theme="@style/AppTheme"> + android:name=".activities.MainActivity" + android:label="@string/title_activity_main" /> + + + diff --git a/app/src/main/java/io/leftshift/androidm/activity/AccountActivity.java b/app/src/main/java/io/leftshift/androidm/activity/AccountActivity.java new file mode 100644 index 0000000..5fa0127 --- /dev/null +++ b/app/src/main/java/io/leftshift/androidm/activity/AccountActivity.java @@ -0,0 +1,171 @@ +package io.leftshift.androidm.activity; + +import android.Manifest; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.Activity; +import android.content.DialogInterface; +import android.content.pm.PackageManager; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.Fragment; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.FrameLayout; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import io.leftshift.androidm.fragment.AccountFragment; +import io.leftshift.androidm.utils.Utils; +import io.leftshift.sample.R; + +/** + * @author Akshay Mukadam + * LeftShift Technologies Private Limited + * @since 5/12/2015 + */ +public class AccountActivity extends BaseActivity { + + // Never exceed the request code more that 250 + private final int REQUEST_CODE_ACCOUNT_PERMISSION = 101; + + // Tag name for fragment + private final String ACCOUNT_TAG = "Account_fragment"; + @Bind(R.id.content_layout) + FrameLayout frameLayout; + private List mList; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_account); + ButterKnife.bind(this); + mList = new ArrayList<>(); + if (checkAccountPermission()) { + loadAccountsFragment(); + } + } + + public List getAccounts() { + mList.clear(); + String emailStr = ""; + Account[] accounts = AccountManager.get(this).getAccountsByType("com.google"); + for (Account account : accounts) { + emailStr = account.name; + if (!TextUtils.isEmpty(emailStr)) + mList.add(emailStr); + } + return mList; + } + + /** + * Check Account permission is granted or not. If permission is not granted request permission + * from user + * + * @return + */ + private boolean checkAccountPermission() { + // check if permission is granted use ContextCompat for backward compatibility/ + if (ContextCompat.checkSelfPermission(this, Manifest.permission.GET_ACCOUNTS) != PackageManager.PERMISSION_GRANTED) { + /** + * Check if permission is denied previously by user. If permission is denied educate the + * user why a particular permission is necessary. If app has ask permission for first time + * show permission directly to user. If the user has clicked on never show me this it will + * automatically show the message permission denied + */ + if (ActivityCompat.shouldShowRequestPermissionRationale(AccountActivity.this, Manifest.permission.GET_ACCOUNTS)) { + showPermissionDialog(AccountActivity.this, getString(R.string.contact_permission + ), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + requestPermission(AccountActivity.this); + } + }, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Utils.showSnackBar(AccountActivity.this, getString(R.string.permission_denied), findViewById(android.R.id.content)); + } + }); + return false; + } + + // If app has asked permission for first time request permission directly + requestPermission(AccountActivity.this); + return false; + } else { + return true; + } + + } + + /** + * Request permission from user + * + * @param activity + */ + private void requestPermission(AppCompatActivity activity) { + ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.GET_ACCOUNTS}, REQUEST_CODE_ACCOUNT_PERMISSION); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_account, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + switch (requestCode) { + case REQUEST_CODE_ACCOUNT_PERMISSION: + // grant result will always be either PERMISSION_GRANTED/PERMISSION_DENIED it will never be @null + if ((grantResults[0] == PackageManager.PERMISSION_GRANTED)) { + loadAccountsFragment(); + } else { + loadAccountsFragment(); + Utils.showSnackBar(AccountActivity.this, getString(R.string.permission_denied), findViewById(android.R.id.content)); + } + break; + } + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + private void loadAccountsFragment() { + /** + * Use TAG while commit Fragment which provides avoid the fragment to call + * onCreateView twice when permission is denied externally by user.By using tag we avoid + * creating new instance of fragment by using findFragmentByTag. A good practice we should + * following while using fragments in app + */ + Fragment fragment = getSupportFragmentManager().findFragmentByTag(ACCOUNT_TAG); + if (fragment == null) { + fragment = new AccountFragment(); + getSupportFragmentManager().beginTransaction().add(R.id.content_layout, fragment, + ACCOUNT_TAG).commitAllowingStateLoss(); + } else { + ((AccountFragment) fragment).initUi(); + } + } + +} diff --git a/app/src/main/java/io/leftshift/androidm/activity/BaseActivity.java b/app/src/main/java/io/leftshift/androidm/activity/BaseActivity.java new file mode 100644 index 0000000..0537b18 --- /dev/null +++ b/app/src/main/java/io/leftshift/androidm/activity/BaseActivity.java @@ -0,0 +1,26 @@ +package io.leftshift.androidm.activity; + +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; + +/** + * Created by akshay on 5/12/15. + */ +public class BaseActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + protected void showPermissionDialog(Context context, String message, DialogInterface.OnClickListener + okClickListener, DialogInterface.OnClickListener cancelOnClickListener) { + new AlertDialog.Builder(context).setMessage(message). + setCancelable(false).setPositiveButton("Ok", okClickListener). + setNegativeButton("CANCEL", cancelOnClickListener) + .show(); + } +} diff --git a/app/src/main/java/io/leftshift/androidm/fragment/AccountFragment.java b/app/src/main/java/io/leftshift/androidm/fragment/AccountFragment.java new file mode 100644 index 0000000..daf7ba7 --- /dev/null +++ b/app/src/main/java/io/leftshift/androidm/fragment/AccountFragment.java @@ -0,0 +1,55 @@ +package io.leftshift.androidm.fragment; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; + +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import io.leftshift.androidm.activity.AccountActivity; +import io.leftshift.sample.R; + +/** + * Created by akshay on 5/12/15. + */ +public class AccountFragment extends BaseFragment { + + @Bind(R.id.auto_email_text_view) + AutoCompleteTextView autoCompleteTextView; + ArrayAdapter mAdapter; + List mList; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_account, container, false); + ButterKnife.bind(this, view); + return view; + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + initUi(); + } + + public void initUi() { + mList = ((AccountActivity) getActivity()).getAccounts(); + if (mList != null) { + autoCompleteTextView.setThreshold(1); + mAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, mList); + autoCompleteTextView.setAdapter(mAdapter); + } + } +} diff --git a/app/src/main/java/io/leftshift/androidm/fragment/BaseFragment.java b/app/src/main/java/io/leftshift/androidm/fragment/BaseFragment.java new file mode 100644 index 0000000..7dd915b --- /dev/null +++ b/app/src/main/java/io/leftshift/androidm/fragment/BaseFragment.java @@ -0,0 +1,25 @@ +package io.leftshift.androidm.fragment; + +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v7.app.AlertDialog; + +/** + * Created by akshay on 5/12/15. + */ +public class BaseFragment extends android.support.v4.app.Fragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + + protected void showPermissionDialog(Context context, String message, DialogInterface.OnClickListener + okClickListener, DialogInterface.OnClickListener cancelOnClickListener) { + new AlertDialog.Builder(context).setMessage(message). + setCancelable(false).setPositiveButton("Ok", okClickListener). + setNegativeButton("CANCEL", cancelOnClickListener) + .show(); + } +} diff --git a/app/src/main/java/io/leftshift/androidm/utils/Utils.java b/app/src/main/java/io/leftshift/androidm/utils/Utils.java new file mode 100644 index 0000000..2984c8d --- /dev/null +++ b/app/src/main/java/io/leftshift/androidm/utils/Utils.java @@ -0,0 +1,45 @@ +package io.leftshift.androidm.utils; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.net.Uri; +import android.provider.Settings; +import android.support.design.widget.Snackbar; +import android.view.View; +import android.widget.TextView; + +import io.leftshift.sample.R; + +/** + * Created by akshay on 5/12/15. + */ +public class Utils { + + public static void showSnackBar(final Context context, String message, View view) { + if (context != null) { + final Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_LONG); + View v = snackbar.getView(); + snackbar.setAction(R.string.app_settings, new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + Uri uri = Uri.fromParts("package", context.getPackageName(), null); + intent.setData(uri); + context.startActivity(intent); + } + }); + v.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + snackbar.dismiss(); + } + }); + TextView textView = (TextView) v.findViewById(android.support.design.R.id.snackbar_text); + textView.setMaxLines(3); + textView.setTextColor(Color.WHITE); + snackbar.show(); + } + } +} diff --git a/app/src/main/java/io/leftshift/sample/activities/HomeActivity.java b/app/src/main/java/io/leftshift/sample/activities/HomeActivity.java new file mode 100644 index 0000000..569bd57 --- /dev/null +++ b/app/src/main/java/io/leftshift/sample/activities/HomeActivity.java @@ -0,0 +1,80 @@ +package io.leftshift.sample.activities; + +import android.content.Intent; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; + +import java.util.Arrays; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import io.leftshift.androidm.activity.AccountActivity; +import io.leftshift.sample.R; +import io.leftshift.sample.adapter.HomeRecyclerAdapter; +import io.leftshift.sample.adapter.HomeRecyclerAdapter.OnItemClick; + +public class HomeActivity extends AppCompatActivity implements OnItemClick { + + + @Bind(R.id.recycler_view) + RecyclerView recyclerView; + LinearLayoutManager mLinearLayoutManager; + + HomeRecyclerAdapter mAdapter; + List mList; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_home); + ButterKnife.bind(this); + + mList = Arrays.asList(getResources().getStringArray(R.array.examples)); + + mLinearLayoutManager = new LinearLayoutManager(this); + recyclerView.setLayoutManager(mLinearLayoutManager); + + mAdapter = new HomeRecyclerAdapter(this, mList, this); + recyclerView.setAdapter(mAdapter); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_home, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + public void setOnItemClickListener(View view, int position) { + switch (position) { + case 0: + startActivity(new Intent(HomeActivity.this, MainActivity.class)); + break; + case 1: + startActivity(new Intent(HomeActivity.this, AccountActivity.class)); + break; + } + } +} diff --git a/app/src/main/java/io/leftshift/sample/adapter/HomeRecyclerAdapter.java b/app/src/main/java/io/leftshift/sample/adapter/HomeRecyclerAdapter.java new file mode 100644 index 0000000..18c8f5d --- /dev/null +++ b/app/src/main/java/io/leftshift/sample/adapter/HomeRecyclerAdapter.java @@ -0,0 +1,71 @@ +package io.leftshift.sample.adapter; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import io.leftshift.sample.R; + +/** + * Created by akshay on 5/12/15. + */ +public class HomeRecyclerAdapter extends RecyclerView.Adapter { + + Context mContext; + List mList; + public OnItemClick mOnItemClick; + + public HomeRecyclerAdapter(Context mContext, List mList, OnItemClick mOnItemClick) { + this.mContext = mContext; + this.mList = mList; + this.mOnItemClick = mOnItemClick; + } + + @Override + public HomeViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) { + LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.row_home, viewGroup, false); + HomeViewHolder homeViewHolder = new HomeViewHolder(view); + return homeViewHolder; + } + + @Override + public void onBindViewHolder(HomeViewHolder homeViewHolder, int position) { + String name = mList.get(position); + homeViewHolder.textView.setText(name); + } + + @Override + public int getItemCount() { + return mList.size(); + } + + class HomeViewHolder extends RecyclerView.ViewHolder { + @Bind(R.id.text_view) + TextView textView; + + public HomeViewHolder(final View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mOnItemClick != null) { + mOnItemClick.setOnItemClickListener(itemView, getAdapterPosition()); + } + } + }); + } + } + + public interface OnItemClick { + void setOnItemClickListener(View view, int position); + } +} diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml new file mode 100644 index 0000000..eaafd7f --- /dev/null +++ b/app/src/main/res/layout/activity_account.xml @@ -0,0 +1,15 @@ + + + + diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml new file mode 100644 index 0000000..7d8362d --- /dev/null +++ b/app/src/main/res/layout/activity_home.xml @@ -0,0 +1,15 @@ + + + + diff --git a/app/src/main/res/layout/fragment_account.xml b/app/src/main/res/layout/fragment_account.xml new file mode 100644 index 0000000..7aacce8 --- /dev/null +++ b/app/src/main/res/layout/fragment_account.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/row_home.xml b/app/src/main/res/layout/row_home.xml new file mode 100644 index 0000000..adc2fe0 --- /dev/null +++ b/app/src/main/res/layout/row_home.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_account.xml b/app/src/main/res/menu/menu_account.xml new file mode 100644 index 0000000..55f103d --- /dev/null +++ b/app/src/main/res/menu/menu_account.xml @@ -0,0 +1,7 @@ + + + diff --git a/app/src/main/res/menu/menu_home.xml b/app/src/main/res/menu/menu_home.xml new file mode 100644 index 0000000..7091db4 --- /dev/null +++ b/app/src/main/res/menu/menu_home.xml @@ -0,0 +1,7 @@ + + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 47c8224..d467078 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -2,4 +2,5 @@ 16dp 16dp + 16sp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 39768c6..0f3686b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,19 @@ - RxJava + LeftShift Samples MainActivity Hello world! Settings + HomeActivity + + + Rx Java + M Permission Model + + AccountActivity + Settings + + Permission Denied, grant permission by clicking settings + LeftShift Samples needs access to your contact + Enter your Email Address