Android

Android TutorialAdding a FuseView to an Android ProjectAndroid 9-Patch ImagesAndroid Accessing SQLite databases using the ContentValues classAndroid Accounts and AccountManagerAndroid ACRAAndroid ActivityAndroid Activity RecognitionAndroid adb (Android Debug Bridge)Android adb shellAndroid AdMobAndroid AIDLAndroid AlarmManagerAndroid Android-x86 in VirtualBoxAndroid Animated AlertDialog BoxAndroid AnimatorsAndroid Annotation ProcessorAndroid Architecture ComponentsAndroid AsyncTaskAndroid AudioManagerAndroid AudioTrackAndroid AuthenticatorAndroid AutoCompleteTextViewAndroid Autosizing TextViewsAndroid Barcode and QR code readingAndroid Bitmap CacheAndroid Bluetooth and Bluetooth LE APIAndroid Bluetooth Low EnergyAndroid Bottom SheetsAndroid BottomNavigationViewAndroid BroadcastReceiverAndroid Building Backwards Compatible AppsAndroid ButterKnifeAndroid ButtonAndroid Callback URLAndroid Camera 2 APIAndroid Camera and GalleryAndroid Canvas drawing using SurfaceViewAndroid Capturing ScreenshotsAndroid CardViewAndroid Check Data ConnectionAndroid Check Internet ConnectivityAndroid CleverTapAndroid ColorsAndroid ConstraintLayoutAndroid ConstraintSetAndroid ContentProviderAndroid ContextAndroid CoordinatorLayout and BehaviorsAndroid Count Down TimerAndroid Crash Reporting ToolsAndroid Create Singleton Class for Toast MessageAndroid Creating Custom ViewsAndroid Creating Overlay (always-on-top) WindowsAndroid Creating Splash screenAndroid Custom FontsAndroid Dagger 2Android Data Binding LibraryAndroid Data Encryption/DecryptionAndroid Data Synchronization with Sync AdapterAndroid Date and Time PickersAndroid DayNight Theme (AppCompat v23.2 / API 14+)Android Define step value (increment) for custom RangeSeekBarAndroid Design PatternsAndroid Device Display MetricsAndroid DialogAndroid Displaying Google AdsAndroid Doze ModeAndroid DrawablesAndroid EditTextAndroid Email ValidationAndroid EmulatorAndroid Enhancing Alert DialogsAndroid ExceptionsAndroid ExoPlayerAndroid FastjsonAndroid fastlaneAndroid FileProviderAndroid FirebaseAndroid Firebase App IndexingAndroid Firebase Cloud MessagingAndroid Firebase Crash ReportingAndroid Firebase Realtime DataBaseAndroid FloatingActionButtonAndroid Formatting phone numbers with pattern.Android Formatting StringsAndroid FragmentsAndroid FrescoAndroid game developmentAndroid Gesture Detection



Android Architecture Components

From WikiOD

Android Architecture Components is new collection of libraries that help you design robust, testable, and maintainable apps. Main parts are: Lifecycles, ViewModel, LiveData, Room.

Using Lifecycle in AppCompatActivity[edit | edit source]

Extend your activity from this activity

public abstract class BaseCompatLifecycleActivity extends AppCompatActivity implements LifecycleRegistryOwner {
    // We need this class, because LifecycleActivity extends FragmentActivity not AppCompatActivity

    @NonNull
    private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);

    @NonNull
    @Override
    public LifecycleRegistry getLifecycle() {
        return lifecycleRegistry;
    }
}

Add Architecture Components[edit | edit source]

Project build.gradle

allprojects {
    repositories {
        jcenter()
        // Add this if you use Gradle 4.0+
        google()
        // Add this if you use Gradle < 4.0
        maven { url 'https://maven.google.com' }
    }
}

ext {
    archVersion = '1.0.0-alpha5'
}

Application build gradle

// For Lifecycles, LiveData, and ViewModel
compile "android.arch.lifecycle:runtime:$archVersion"
compile "android.arch.lifecycle:extensions:$archVersion"
annotationProcessor "android.arch.lifecycle:compiler:$archVersion"

// For Room
compile "android.arch.persistence.room:runtime:$archVersion"
annotationProcessor "android.arch.persistence.room:compiler:$archVersion"

// For testing Room migrations
testCompile "android.arch.persistence.room:testing:$archVersion"

// For Room RxJava support
compile "android.arch.persistence.room:rxjava2:$archVersion"

ViewModel with LiveData transformations[edit | edit source]

public class BaseViewModel extends ViewModel {
    private static final int TAG_SEGMENT_INDEX = 2;
    private static final int VIDEOS_LIMIT = 100;

    // We save input params here
    private final MutableLiveData<Pair<String, String>> urlWithReferrerLiveData = new MutableLiveData<>();

    // transform specific uri param to "tag"
    private final LiveData<String> currentTagLiveData = Transformations.map(urlWithReferrerLiveData, pair -> {
        Uri uri = Uri.parse(pair.first);
        List<String> segments = uri.getPathSegments();
        if (segments.size() > TAG_SEGMENT_INDEX)
            return segments.get(TAG_SEGMENT_INDEX);
        return null;
    });

    // transform "tag" to videos list
    private final LiveData<List<VideoItem>> videoByTagData = Transformations.switchMap(currentTagLiveData, tag -> contentRepository.getVideoByTag(tag, VIDEOS_LIMIT));

    ContentRepository contentRepository;

    public BaseViewModel() {
        // some inits
    }

    public void setUrlWithReferrer(String url, String referrer) {
        // set value activates observers and transformations
        urlWithReferrerLiveData.setValue(new Pair<>(url, referrer));
    }

    public LiveData<List<VideoItem>> getVideoByTagData() {
        return videoByTagData;
    }
}

Somewhere in UI:

public class VideoActivity extends BaseCompatLifecycleActivity {
    private VideoViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Get ViewModel
        viewModel = ViewModelProviders.of(this).get(BaseViewModel.class);
        // Add observer
        viewModel.getVideoByTagData().observe(this, data -> {
            // some checks
            adapter.updateData(data);
        });

        ...
        if (savedInstanceState == null) {
            // init loading only at first creation
            // you just set params and 
            viewModel.setUrlWithReferrer(url, referrer);
        }
}

Room peristence[edit | edit source]

Room require four parts: Database class, DAO classes, Entity classes and Migration classes (now you may use only DDL methods):

Entity classes

// Set custom table name, add indexes
@Entity(tableName = "videos",
        indices = {@Index("title")}
)
public final class VideoItem {
    @PrimaryKey // required
    public long articleId;
    public String title;
    public String url;
}

// Use ForeignKey for setup table relation
@Entity(tableName = "tags",
        indices = {@Index("score"), @Index("videoId"), @Index("value")},
        foreignKeys = @ForeignKey(entity = VideoItem.class,
                parentColumns = "articleId",
                childColumns = "videoId",
                onDelete = ForeignKey.CASCADE)
)
public final class VideoTag {
    @PrimaryKey
    public long id;
    public long videoId;
    public String displayName;
    public String value;
    public double score;
}

DAO classes

@Dao
public interface VideoDao {
    // Create insert with custom conflict strategy
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void saveVideos(List<VideoItem> videos);

    // Simple update
    @Update
    void updateVideos(VideoItem... videos);

    @Query("DELETE FROM tags WHERE videoId = :videoId")
    void deleteTagsByVideoId(long videoId);

    // Custom query, you may use select/delete here
    @Query("SELECT v.* FROM tags t LEFT JOIN videos v ON v.articleId = t.videoId WHERE t.value = :tag ORDER BY updatedAt DESC LIMIT :limit")
    LiveData<List<VideoItem>> getVideosByTag(String tag, int limit);
}

Database class

// register your entities and DAOs
@Database(entities = {VideoItem.class, VideoTag.class}, version = 2)
public abstract class ContentDatabase extends RoomDatabase {
    public abstract VideoDao videoDao();
}

Migrations

public final class Migrations {
    private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            final String[] sqlQueries = {
                    "CREATE TABLE IF NOT EXISTS `tags` (`id` INTEGER PRIMARY KEY AUTOINCREMENT," +
                            " `videoId` INTEGER, `displayName` TEXT, `value` TEXT, `score` REAL," +
                            " FOREIGN KEY(`videoId`) REFERENCES `videos`(`articleId`)" +
                            " ON UPDATE NO ACTION ON DELETE CASCADE )",
                    "CREATE  INDEX `index_tags_score` ON `tags` (`score`)",
                    "CREATE  INDEX `index_tags_videoId` ON `tags` (`videoId`)"};
            for (String query : sqlQueries) {
                database.execSQL(query);
            }
        }
    };

    public static final Migration[] ALL = {MIGRATION_1_2};

    private Migrations() {
    }
}

Use in Application class or provide via Dagger

ContentDatabase provideContentDatabase() {
    return Room.databaseBuilder(context, ContentDatabase.class, "data.db")
            .addMigrations(Migrations.ALL).build();
}

Write your repository:

public final class ContentRepository {
    private final ContentDatabase db;
    private final VideoDao videoDao;

    public ContentRepository(ContentDatabase contentDatabase, VideoDao videoDao) {
        this.db = contentDatabase;
        this.videoDao = videoDao;
    }

    public LiveData<List<VideoItem>> getVideoByTag(@Nullable String tag, int limit) {
        // you may fetch from network, save to database
        ....
        return videoDao.getVideosByTag(tag, limit);
    }
}

Use in ViewModel:

ContentRepository contentRepository = ...;
contentRepository.getVideoByTag(tag, limit);

Custom LiveData[edit | edit source]

You may write custom LiveData, if you need custom logic.

Don't write custom class, if you only need to transform data (use Transformations class)

public class LocationLiveData extends LiveData<Location> {
    private LocationManager locationManager;

    private LocationListener listener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            setValue(location);
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
            // Do something
        }

        @Override
        public void onProviderEnabled(String provider) {
            // Do something
        }

        @Override
        public void onProviderDisabled(String provider) {
            // Do something
        }
    };

    public LocationLiveData(Context context) {
        locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    }

    @Override
    protected void onActive() {
        // We have observers, start working
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
    }

    @Override
    protected void onInactive() {
        // We have no observers, stop working
        locationManager.removeUpdates(listener);
    }
}

Custom Lifecycle-aware component[edit | edit source]

Each UI component lifecycle changed as shown at image.

lifecycle state diagram

You may create component, that will be notified on lifecycle state change:

public class MyLocationListener implements LifecycleObserver {
    private boolean enabled = false;
    private Lifecycle lifecycle;
    public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
       ...
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void start() {
        if (enabled) {
           // connect
        }
    }

    public void enable() {
        enabled = true;
        if (lifecycle.getState().isAtLeast(STARTED)) {
            // connect if not connected
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void stop() {
        // disconnect if connected
    }
}

Credit:Stack_Overflow_Documentation