Текст книги "Разработка Android-приложений в деталях"
Автор книги: Тимур Машнин
Жанр: Компьютеры: прочее, Компьютеры
Возрастные ограничения: +12
сообщить о неприемлемом содержимом
Текущая страница: 12 (всего у книги 20 страниц)
Использование нескольких Google аккаунтов для модулей App Engine
В файл buld.gradle backend модуля добавьте:
appengine {
downloadSdk = true
appcfg {
oauth2 = true
noCookies = true
email = «google email»
}
endpoints {
getClientLibsOnBuild = true
getDiscoveryDocsOnBuild = true
}
}
В окне Terminal Android Studio наберите:
gradlew task
Затем наберите:
gradlew appengineUpdate
После появления oauth2 запроса:
Скопируйте строку и вставьте ее в окне Terminal, нажмите Enter.
В результате backend модуль будет развернут по указанному аккаунту.
Android Design Support Library
Android Design Support Library обеспечивает фреймворк, позволяющий создавать приложения, включающие в себя навигационное представление navigation drawer view, плавающую метку для редактируемого текста floating labels for editing text, плавающую кнопку действия floating action button, snackbar, компоновки CoordinatorLayout и AppBarLayout, панель инструментов Toolbar вместо Action Bar, RecyclerView вместо ListView и др.
Наиболее полно отображает использование библиотеки Android Design Support Library шаблон проекта Navigation Drawer Activity среды разработки Android Studio.
Класс главной активности проекта расширяет класс android.support. v7.app. AppCompatActivity и реализует интерфейс android.support.design.widget.NavigationView. OnNavigationItemSelectedListener.
Класс AppCompatActivity здесь дает возможность использования методов setSupportActionBar (toolbar) определяет Toolbar как ActionBar и onBackPressed () вызывается системой при нажатии кнопки назад.
Интерфейс NavigationView. OnNavigationItemSelectedListener дает метод onNavigationItemSelected (MenuItem item), вызываемый системой при выборе элемента navigation drawer view.
Компоновка главной активности начинается с корневого элемента android.support. v4.widget. DrawerLayout. Это является обязательным условием для того, чтобы добавить в приложение панель Navigation Drawer, отображающую основные элементы навигации приложения по левому краю экрана.
Атрибут android: fitsSystemWindows элемента DrawerLayout установлен в true, что дает достаточный отступ, чтобы не было наложения на строку состояния status bar.
Атрибут tools: openDrawer=«start» не влияет на работу приложения и обеспечивает отображение навигационного ящика при открытии компоновки во вкладке Design редактора Android Studio.
Первый дочерний элемент компоновки главной активности содержит компоновку основного содержимого активности, отображаемого, когда навигационный ящик скрыт. Атрибуты android: layout_width=«match_parent» и android: layout_height=«match_parent» обеспечивают заполнение всего окна основным контентом.
Второй элемент android.support.design.widget.NavigationView компоновки главной активности объявляет содержимое навигационного ящика. Атрибуты android: layout_width=«wrap_content» и android: layout_height=«match_parent» обеспечивают ширину равную содержимому и высоту равную высоте окна. Ширину навигационного ящика можно сделать фиксированной, например, 320dp. Атрибут android: layout_gravity=«start» обеспечивает отображение навигационного ящика на левой стороне окна. Атрибут app: headerLayout указывает компоновку заголовка навигационного ящика, атрибут app: menu указывает XML ресурс элементов навигационного ящика.
В компоновке заголовка навигационного ящика значение атрибута android: layout_height корневого элемента LinearLayout можем установить в android: layout_height=«wrap_content». Можно изменить форму и цвет фона заголовка в файле side_nav_bar. xml, например:
<shape xmlns: android="http://schemas.android.com/apk/res/android"
android: shape=«oval»>
<gradient
android: startColor=«#B87F7F»
android: centerColor=«#8C5A5A»
android: endColor=«#7E4949»
android: type=«linear»
android: angle=«135»/>
</shape>
В заголовке можно убрать значок и расположить текстовый элемент по центру, например:
<?xml version=«1.0» encoding=«utf-8»? >
<LinearLayout xmlns: android="http://schemas.android.com/apk/res/android"
android: layout_width=«match_parent» android: layout_height=«wrap_content»
android: background="@drawable/side_nav_bar»
android: paddingBottom=«5dp»
android: paddingTop=«5dp»
android: theme="@style/ThemeOverlay. AppCompat. Dark» android: orientation=«vertical»
android: gravity=«bottom»>
<TextView android: layout_width=«match_parent» android: layout_height=«wrap_content»
android: text=«Содержание»
android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium»
android: textAlignment=«center» />
</LinearLayout>
С помощью атрибутов android: background="@color/nav_drawer_bg», app: itemTextColor="@color/item_drawer» и app: itemBackground="@drawable/navigation_drawer_background» элемента NavigationView можно установить фон навигационного ящика, цвет и фон элементов навигационного ящика. Фон элементов навигационного ящика здесь определяется файлом navigation_drawer_background. xml папки drawable:
<?xml version=«1.0» encoding=«utf-8»? >
<selector xmlns: android="http://schemas.android.com/apk/res/android">
<item android: state_checked=«true» android: drawable="@color/item_checked» />
</selector>
Цвета определяются файлом colors. xml папки values:
<?xml version=«1.0» encoding=«utf-8»? >
<resources>
<color name=«colorPrimary»> #8C5A5A </color>
<color name=«colorPrimaryDark»> #7E4949 </color>
<color name=«colorAccent»> #FF4081 </color>
<color name=«nav_drawer_bg»> #C6A9A9 </color>
<color name=«item_checked»> #FFFFFF </color>
<color name=«item_drawer»> #3B1B1B </color>
</resources>
Размер текста элементов навигационного ящика NavigationView можно установить с помощью атрибута android: theme="@style/NavigationViewStyle» и стиля:
<style name=«NavigationViewStyle»>
<item name=«android: textSize»> 22sp </item>
</style>
Для установки элементов навигационного ящика нужно изменить содержимое файла activity_main_drawer. xml папки menu, например:
<?xml version=«1.0» encoding=«utf-8»? >
<menu xmlns: android="http://schemas.android.com/apk/res/android">
<group android: checkableBehavior=«single»>
<item android: id="@+id/test_1»
android: title=«Тест 1» />
<item android: id="@+id/test_2»
android: title=«Тест 2» />
<item android: id="@+id/test_3»
android: title=«Тест 3» />
<item android: id="@+id/test_4»
android: title=«Тест 4» />
</group>
<item android: title=«Ответы тестов»>
<menu>
<group android: checkableBehavior=«single»>
<item
android: id="@+id/a_test_1»
android: title=«Тест 1» />
<item
android: id="@+id/a_test_2»
android: title=«Тест 2» />
<item
android: id="@+id/a_test_3»
android: title=«Тест 3» />
<item
android: id="@+id/a_test_4»
android: title=«Тест 4» />
</group>
</menu>
</item>
</menu>
В методе onCreate класса главной активности следующий код отвечает за появление в панели инструментов значка гамбургера, при нажатии на который выскакивает навигационный ящик:
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle (this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
toggle.syncState ();
Вызов метода drawer.setDrawerListener (toggle) обеспечивает анимацию значка гамбургера при движении навигационного ящика.
Первый дочерний элемент компоновки главной активности указывает компоновку основного содержимого активности, отображаемого, когда навигационный ящик скрыт. Корневым элементом этой компоновки служит android.support.design.widget.CoordinatorLayout – ViewGroup, обеспечивающий такие эффекты, как перемещение кнопки действия Floating Action Button вверх и вниз, чтобы освободить место для Snackbar, отображение и скрытие панели инструментов Toolbar и кнопки действия FAB при прокрутке списка содержания вниз и вверх.
Первый дочерний элемент CoordinatorLayout это элемент android.support.design. widget. AppBarLayout, обеспечивающий, чтобы вложенный элемент android.support.v7.widget.Toolbar реагировал на прокрутку.
Здесь Toolbar выступает как ActionBar. Для этого в манифесте AndroidManifest. xml стиль активности объявлен как android:theme="@style/AppTheme.NoActionBar» и в методе onCreate активности вызван метод setSupportActionBar (toolbar).
Атрибуты android: layout_height=»? attr/actionBarSize"и app: popupTheme="@style/AppTheme. PopupOverlay» обеспечивают режим наложения при скрытии и показе панели инструметов. Для того чтобы Toolbar реагировал на прокрутку также нужно добавить атрибут app: layout_scrollFlags=«scroll|enterAlways».
Чтобы определить связь между AppBarLayout и видом, который будет прокручиваться, нужно добавить атрибут app: layout_behavior к любому виду, способному содержать прокрутку, такому как NestedScrollView. Поэтому в файле компоновки content_main. xml обернем элемент RelativeLayout в элемент NestedScrollView:
<?xml version=«1.0» encoding=«utf-8»? >
<android.support.v4.widget.NestedScrollView
xmlns: android="http://schemas.android.com/apk/res/android"
xmlns: app="http://schemas.android.com/apk/res-auto"
xmlns: tools="http://schemas.android.com/tools"
android: layout_width=«match_parent»
android: layout_height=«match_parent»
android: paddingLeft="@dimen/activity_horizontal_margin»
android: paddingRight="@dimen/activity_horizontal_margin»
android: paddingTop="@dimen/activity_vertical_margin»
android: paddingBottom="@dimen/activity_vertical_margin»
android: fillViewport=«true»
android: layout_gravity=«fill_vertical»
app: layout_behavior="@string/appbar_scrolling_view_behavior»
tools:context=".MainActivity»
tools: showIn="@layout/app_bar_main»
>
<RelativeLayout
android: layout_width=«match_parent»
android: layout_height=«match_parent»>
<TextView android: text=«Hello World!»
android: layout_width=«wrap_content»
android: layout_height=«wrap_content» />
</RelativeLayout>
</android.support.v4.widget.NestedScrollView>
Теперь при прокрутке основного содержимого активности панель инструментов Toolbar будет отображаться и скрываться.
Для того чтобы кнопка действия FAB реагировала на прокрутку содержания вниз и вверх, добавим атрибут app:layout_behavior="com.example.ScrollFABBehavior» в элемент android.support.design. widget. FloatingActionButton и создадим класс ScrollFABBehavior:
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.support.design. widget. FloatingActionButton;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.View;
public class ScrollFABBehavior extends FloatingActionButton.Behavior {
public ScrollFABBehavior (Context context, AttributeSet attrs) {
super ();
}
@Override
public boolean onStartNestedScroll (final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
final View directTargetChild, final View target, final int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
|| super. onStartNestedScroll (coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public void onNestedScroll (final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
final View target, final int dxConsumed, final int dyConsumed,
final int dxUnconsumed, final int dyUnconsumed) {
super. onNestedScroll (coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
if (dyConsumed> 0 && child.getVisibility () == View.VISIBLE) {
child. hide ();
} else if (dyConsumed <0 && child.getVisibility ()!= View.VISIBLE) {
child.show ();
}
}
}
Изменить значок кнопки действия FAB можно с помощью атрибута android: src="@android: drawable/ic_menu_send».
Кнопку действия FAB можно также расположить посередине экрана, например:
<?xml version=«1.0» encoding=«utf-8»? >
<android.support.design.widget.CoordinatorLayout
xmlns: android="http://schemas.android.com/apk/res/android"
xmlns: app="http://schemas.android.com/apk/res-auto"
xmlns: tools="http://schemas.android.com/tools"
android: layout_width=«match_parent»
android: layout_height=«match_parent»
android: fitsSystemWindows=«true»
tools:context=".MainActivity»>
<android.support.design. widget. AppBarLayout
android: layout_height=«wrap_content»
android: layout_width=«match_parent»
android: theme="@style/AppTheme. AppBarOverlay»>
<android.support.v7.widget.Toolbar
android: id="@+id/toolbar»
android: layout_width=«match_parent»
android: layout_height=»? attr/actionBarSize»
android: background=»? attr/colorPrimary»
app: popupTheme="@style/AppTheme. PopupOverlay»
app: layout_scrollFlags=«scroll|enterAlways»
/>
</android.support.design. widget. AppBarLayout>
<android.support.v4.widget.NestedScrollView
xmlns: android="http://schemas.android.com/apk/res/android"
xmlns: app="http://schemas.android.com/apk/res-auto"
xmlns: tools="http://schemas.android.com/tools"
android: layout_width=«match_parent»
android: layout_height=«wrap_content»
android: paddingLeft="@dimen/activity_horizontal_margin»
android: paddingRight="@dimen/activity_horizontal_margin»
android: paddingTop="@dimen/activity_vertical_margin»
android: paddingBottom="@dimen/activity_vertical_margin»
android: fillViewport=«true»
android: layout_gravity=«fill_vertical»
app: layout_behavior="@string/appbar_scrolling_view_behavior»
tools:context=".MainActivity»
tools: showIn="@layout/app_bar_main»
android: id="@+id/sc_view»
>
<RelativeLayout
android: layout_width=«match_parent»
android: layout_height=«wrap_content»
>
<RelativeLayout
android: layout_width=«match_parent»
android: layout_height=«wrap_content»
android: id="@+id/content»
>
<TextView android: text="@string/def_test_title»
android: id="@+id/num_test»
android: layout_width=«wrap_content»
android: layout_height=«wrap_content»
style="@android:style/TextAppearance.Large»
android: textStyle=«bold»/>
</RelativeLayout>
<RelativeLayout
android: layout_width=«match_parent»
android: layout_height=«wrap_content»
android: layout_below="@+id/content»
android: layout_alignParentLeft=«true»
android: layout_alignParentStart=«true»
android: paddingTop=«96dp»
>
<TableLayout
android: layout_width=«wrap_content»
android: layout_height=«wrap_content»
android: layout_alignParentBottom=«true»>
<TableRow
android: layout_width=«wrap_content»
android: layout_height=«wrap_content»
android: layout_centerVertical=«true»
android: layout_alignParentLeft=«true»
android: layout_alignParentStart=«true»>
</TableRow>
</TableLayout>
</RelativeLayout>
</RelativeLayout>
</android.support.v4.widget.NestedScrollView>
<android.support.design. widget. FloatingActionButton
android: id="@+id/fab»
android: layout_width=«wrap_content»
android: layout_height=«wrap_content»
android: layout_gravity=«bottom|end»
android: layout_margin="@dimen/fab_margin»
android: src="@android: drawable/ic_input_add» app:layout_behavior="com.example.ScrollFABBehavior»
app: layout_anchor="@id/content»
app: layout_anchorGravity=«bottom|center»
/>
</android.support.design.widget.CoordinatorLayout>
Изменить фон сообщения Snackbar, отображаемого при нажатии кнопки FAB можно следующим образом:
Snackbar snack = Snackbar.make (view, result, Snackbar. LENGTH_LONG);
View snackbarView = snack.getView ();
snackbarView.setBackgroundColor(Color.parseColor («#7E4949»));
snack.setAction («Action», null).show ();
Пример приложения Baby Tracking
Данное приложение периодически собирает информацию о местоположении телефона и сохраняет ее. Временной интервал сбора информации выставляется в настройках приложения.
Собранная информация сохраняется и передается в облако. Временной период, за который информация сохраняется, также определяется в настройках приложения.
Интерфейс приложения позволяет запустить трекинг, отключить трекинг и очистить сохраненные данные. Доступ к интерфейсу приложения осуществляется по логину-паролю. Настойки приложения позволяют изменить логин-пароль.
Приложение позволяет отобразить сохраненный трекинг, загружая и отображая переданные в облако данные.
При запущенном трекинге перезагрузка телефона не останавливает трекинг.
Файл манифеста приложения:
<?xml version=«1.0» encoding=«utf-8»? >
<manifest xmlns: android="http://schemas.android.com/apk/res/android"
package="com.babytracking»>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED» />
<uses-permission android:name="android.permission. WAKE_LOCK» />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION» />
<application
android: allowBackup=«true»
android: icon="@mipmap/ic_launcher»
android: label="@string/app_name»
android: supportsRtl=«true»
android: theme="@style/AppTheme»>
<activity
android: name=». LoginActivity»
android: label="@string/title_activity_login»>
<intent-filter android: label="@string/app_name»>
<action android:name="android.intent.action.MAIN» />
<category android:name="android.intent.category.LAUNCHER» />
</intent-filter>
</activity>
<service
android: name=». LocationIntentService»
android: exported=«false»>
</service>
<receiver
android: name=». LocationReceiver»
android: enabled=«true»
android: exported=«true»
android: process=":remote»>
</receiver>
<activity
android:name=".MainActivity»
android: label="@string/app_name»
android:theme="@style/AppTheme.NoActionBar»>
</activity>
<service
android:name=".StartAlarmIntentService»
android: exported=«false»>
</service>
<receiver
android:name=".BootReceiver»
android: enabled=«true»
android: exported=«true»>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED» />
</intent-filter>
</receiver>
<meta-data
android:name="com.google.android.gms.version»
android: value="@integer/google_play_services_version» />
</application>
<activity
android:name="com.google.android.gms.ads.AdActivity»
android: configChanges=«keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize»
android:theme="@android:style/Theme.Translucent» />
</manifest>
Приложение начинается с активности, обеспечивающей авторизированный доступ к интерфейсу приложения.
Здесь атрибут android: label="@string/title_activity_login» активности обеспечивает показ заголовка, отличного от заголовка приложения.
Код этой активности:
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation. TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android. os. Build;
import android. os. Bundle;
import android.support. v7.app. AppCompatActivity;
import android. text. TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.view.View. OnClickListener;
import android.view.inputmethod. EditorInfo;
import android. widget. Button;
import android. widget. EditText;
import android. widget. TextView;
public class LoginActivity extends AppCompatActivity {
private static final String DUMMY_CREDENTIALS_LOGIN = new String («admin»);
private static final String DUMMY_CREDENTIALS_PASSWORD = new String («1234»);
private UserLoginTask mAuthTask = null;
// UI references.
private EditText mLoginView;
private EditText mPasswordView;
private View mProgressView;
private View mLoginFormView;
private SharedPreferences mSettings;
private Activity activity;
@Override
protected void onCreate (Bundle savedInstanceState) {
super. onCreate (savedInstanceState);
setContentView(R.layout.activity_login);
activity=this;
// Set up the login form.
mLoginView = (EditText) findViewById(R.id.email);
mPasswordView = (EditText) findViewById(R.id.password);
mPasswordView.setOnEditorActionListener (new TextView. OnEditorActionListener () {
@Override
public boolean onEditorAction (TextView textView, int id, KeyEvent keyEvent) {
if (id == R.id.login || id == EditorInfo.IME_NULL) {
attemptLogin ();
return true;
}
return false;
}
});
mSettings = getSharedPreferences («APP_PREFERENCES», Context.MODE_PRIVATE);
if(!mSettings.contains («APP_PREFERENCES_LOGIN»)) {
mLoginView.setHint (DUMMY_CREDENTIALS_LOGIN);
} else {
String prefLogin=mSettings.getString («APP_PREFERENCES_LOGIN»,»»);
mLoginView.setHint (prefLogin);
}
if(!mSettings.contains («APP_PREFERENCES_PASSWORD»)) {
mPasswordView.setHint (DUMMY_CREDENTIALS_PASSWORD);
} else {
mPasswordView.setHint («Password»);
}
Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button);
mEmailSignInButton.setOnClickListener (new OnClickListener () {
@Override
public void onClick (View view) {
attemptLogin ();
}
});
mLoginFormView = findViewById(R.id.login_form);
mProgressView = findViewById(R.id.login_progress);
}
/**
* Attempts to sign in or register the account specified by the login form.
* If there are form errors (invalid email, missing fields, etc.), the
* errors are presented and no actual login attempt is made.
*/
private void attemptLogin () {
if (mAuthTask!= null) {
return;
}
// Reset errors.
mLoginView.setError (null);
mPasswordView.setError (null);
// Store values at the time of the login attempt.
String login = mLoginView.getText().toString ();
String password = mPasswordView.getText().toString ();
boolean cancel = false;
View focusView = null;
if (TextUtils.isEmpty (password)) {
mPasswordView.setError(getString(R.string.error_field_required));
focusView = mPasswordView;
cancel = true;
}
if (TextUtils.isEmpty (login)) {
mLoginView.setError(getString(R.string.error_field_required));
focusView = mLoginView;
cancel = true;
}
if (cancel) {
focusView.requestFocus ();
} else {
showProgress (true);
mAuthTask = new UserLoginTask (login, password);
mAuthTask. execute ((Void) null);
}
}
/**
* Shows the progress UI and hides the login form.
*/
@TargetApi(Build.VERSION_CODES. HONEYCOMB_MR2)
private void showProgress (final boolean show) {
// On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
// for very easy animations. If available, use these APIs to fade-in
// the progress spinner.
if (Build.VERSION.SDK_INT> = Build.VERSION_CODES. HONEYCOMB_MR2) {
int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
mLoginFormView.setVisibility (show? View. GONE: View.VISIBLE);
mLoginFormView.animate().setDuration(shortAnimTime).alpha (
show? 0: 1).setListener (new AnimatorListenerAdapter () {
@Override
public void onAnimationEnd (Animator animation) {
mLoginFormView.setVisibility (show? View. GONE: View.VISIBLE);
}
});
mProgressView.setVisibility (show? View.VISIBLE: View. GONE);
mProgressView.animate().setDuration(shortAnimTime).alpha (
show? 1: 0).setListener (new AnimatorListenerAdapter () {
@Override
public void onAnimationEnd (Animator animation) {
mProgressView.setVisibility (show? View.VISIBLE: View. GONE);
}
});
} else {
// The ViewPropertyAnimator APIs are not available, so simply show
// and hide the relevant UI components.
mProgressView.setVisibility (show? View.VISIBLE: View. GONE);
mLoginFormView.setVisibility (show? View. GONE: View.VISIBLE);
}
}
/**
* Represents an asynchronous login/registration task used to authenticate
* the user.
*/
public class UserLoginTask extends AsyncTask <Void, Void, Boolean> {
private final String mLogin;
private final String mPassword;
UserLoginTask (String login, String password) {
mLogin = login;
mPassword = password;
}
@Override
protected Boolean doInBackground (Void… params) {
Boolean success=true;
if(!mSettings.contains («APP_PREFERENCES_LOGIN»)) {
if (!DUMMY_CREDENTIALS_LOGIN. equals (mLogin)) success=false;
} else {
String prefLogin=mSettings.getString («APP_PREFERENCES_LOGIN»,»»);
if (!prefLogin. equals (mLogin)) success=false;
}
if(!mSettings.contains («APP_PREFERENCES_PASSWORD»)) {
if (!DUMMY_CREDENTIALS_PASSWORD. equals (mPassword)) success=false;
} else {
String prefLogin=mSettings.getString («APP_PREFERENCES_PASSWORD»,»»);
if (!prefLogin. equals (mPassword)) success=false;
}
return success;
}
@Override
protected void onPostExecute (final Boolean success) {
mAuthTask = null;
showProgress (false);
if (success) {
Intent intent = new Intent (activity, MainActivity.class);
startActivity (intent);
} else {
mPasswordView.setError(getString(R.string.error_incorrect_password));
mPasswordView.requestFocus ();
}
}
@Override
protected void onCancelled () {
mAuthTask = null;
showProgress (false);
}
}
@Override
protected void onStop () {
this.finish ();
super. onStop ();
}
}
Файл компоновки этой активности:
<LinearLayout xmlns: android="http://schemas.android.com/apk/res/android"
xmlns: tools="http://schemas.android.com/tools" android: layout_width=«match_parent»
android: layout_height=«match_parent» android: gravity=«center_horizontal»
android: orientation=«vertical» android: paddingBottom="@dimen/activity_vertical_margin»
android: paddingLeft="@dimen/activity_horizontal_margin»
android: paddingRight="@dimen/activity_horizontal_margin»
android: paddingTop="@dimen/activity_vertical_margin»
tools:context="com.tmsoftstudio.babytracking. LoginActivity»>
<! – Login progress – >
<ProgressBar android: id="@+id/login_progress» style=»? android: attr/progressBarStyleLarge»
android: layout_width=«wrap_content» android: layout_height=«wrap_content»
android: layout_marginBottom=«8dp» android: visibility=«gone» />
<ScrollView android: id="@+id/login_form» android: layout_width=«match_parent»
android: layout_height=«match_parent»>
<LinearLayout android: id="@+id/email_login_form» android: layout_width=«match_parent»
android: layout_height=«wrap_content» android: orientation=«vertical»>
<android.support.design. widget. TextInputLayout android: layout_width=«match_parent»
android: layout_height=«wrap_content»>
<EditText
android: id="@+id/email»
android: layout_width=«match_parent»
android: layout_height=«wrap_content»
android: inputType=«textEmailAddress»
android: maxLines=«1»
android: singleLine=«true» />
</android.support.design. widget. TextInputLayout>
<android.support.design. widget. TextInputLayout android: layout_width=«match_parent»
android: layout_height=«wrap_content»>
<EditText android: id="@+id/password»
android: layout_width=«match_parent»
android: layout_height=«wrap_content»
android: imeActionId="@+id/login»
android: imeActionLabel="@string/action_sign_in_short»
android: imeOptions=«actionUnspecified»
android: inputType=«textPassword»
android: maxLines=«1»
android: singleLine=«true» />
</android.support.design. widget. TextInputLayout>
<Button android: id="@+id/email_sign_in_button»
style=»? android: textAppearanceSmall»
android: layout_width=«match_parent»
android: layout_height=«wrap_content»
android: layout_marginTop=«16dp»
android: text="@string/action_sign_in»
android: textStyle=«bold» />
</LinearLayout>
</ScrollView>
</LinearLayout>
Активность отображает два поля для ввода логина и пароля и кнопку, при нажатии на которую происходит попытка авторизации.
Первоначально поля показывают в виде подсказок логин и пароль, установленные по умолчанию. В дальнейшем, после изменения логина и пароля, только поле логина показывает в виде подсказки установленное значение.
Значения логина и пароля сохраняются в файле SharedPreferences. Соответственно, при попытке авторизации, введенные пользователем значения сравниваются со значениями файла SharedPreferences.
Вызов метода finish () в методе onStop () исключает возврат к уже открытой активности и заставляет авторизироваться заново.
После успешной авторизации вызывается главная активность приложения:
import android.app.AlarmManager;
import android.app.AlertDialog;
import android. app. Dialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content. DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.graphics.Color;
import android. os. Bundle;
import android.support.design. widget. FloatingActionButton;
import android.support.design.widget.NavigationView;
import android.support.design.widget.Snackbar;
import android.support. v4.app. DialogFragment;
import android.support.v4.view.GravityCompat;
import android.support. v4.widget. DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support. v7.app. AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android. util. TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android. widget. Button;
import android. widget. EditText;
import android. widget. TextView;
import android.widget.Toast;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;
import com.google.android.gms. appindexing. AppIndex;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common. GooglePlayServicesUtil;
import com.google.android.gms.common. api. GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms. drive. Drive;
import com.google.android.gms. drive. DriveFolder;
import com.google.android.gms. drive. DriveId;
import com.google.android.gms. drive. DriveResource;
import com.google.android.gms.drive.MetadataChangeSet;
public class MainActivity extends AppCompatActivity
implements NavigationView. OnNavigationItemSelectedListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient. OnConnectionFailedListener {
private SharedPreferences mSettings;
private Context context;
private static long TRACKING_INTERVAL=15L*1000*60;
private static long SAVING_INTERVAL=24L*1000*60*60;
private GoogleApiClient mGoogleDriveApiClient;
private static final int RESOLVE_CONNECTION_REQUEST_CODE=1;
@Override
protected void onCreate (Bundle savedInstanceState) {
super. onCreate (savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar (toolbar);
context = this;
mSettings = getSharedPreferences («APP_PREFERENCES», Context.MODE_PRIVATE);
AdView mAdView = (AdView) findViewById(R.id.adViewBan);
AdRequest adRequest = new AdRequest. Builder ().build ();
mAdView. loadAd (adRequest);
if (!mSettings.contains («TRACKING_INTERVAL»)) {
SharedPreferences. Editor editor = mSettings. edit ();
editor. putLong («TRACKING_INTERVAL», TRACKING_INTERVAL);
editor.commit ();
}
if (!mSettings.contains («SAVING_INTERVAL»)) {
SharedPreferences. Editor editor = mSettings. edit ();
Правообладателям!
Это произведение, предположительно, находится в статусе 'public domain'. Если это не так и размещение материала нарушает чьи-либо права, то сообщите нам об этом.